mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 05:48:21 +01:00
Merge pull request #995 from rectorphp/nts-routing
Add RouterListToControllerAnnotationsRector
This commit is contained in:
commit
f29aae36e9
@ -38,6 +38,7 @@
|
||||
"Rector\\DeadCode\\": "packages/DeadCode/src",
|
||||
"Rector\\Guzzle\\": "packages/Guzzle/src",
|
||||
"Rector\\CodeQuality\\": "packages/CodeQuality/src",
|
||||
"Rector\\NetteToSymfony\\": "packages/NetteToSymfony/src",
|
||||
"Rector\\DomainDrivenDesign\\": "packages/DomainDrivenDesign/src",
|
||||
"Rector\\NodeTypeResolver\\": "packages/NodeTypeResolver/src",
|
||||
"Rector\\Symfony\\": "packages/Symfony/src",
|
||||
@ -63,6 +64,7 @@
|
||||
"Rector\\CakePHP\\Tests\\": "packages/CakePHP/tests",
|
||||
"Rector\\CodeQuality\\Tests\\": "packages/CodeQuality/tests",
|
||||
"Rector\\DeadCode\\Tests\\": "packages/DeadCode/tests",
|
||||
"Rector\\NetteToSymfony\\Tests\\": "packages/NetteToSymfony/tests",
|
||||
"Rector\\CodingStyle\\Tests\\": "packages/CodingStyle/tests",
|
||||
"Rector\\DomainDrivenDesign\\Tests\\": "packages/DomainDrivenDesign/tests",
|
||||
"Rector\\Guzzle\\Tests\\": "packages/Guzzle/tests",
|
||||
|
@ -36,6 +36,7 @@
|
||||
- [DomainDrivenDesign\ObjectToScalar](#domaindrivendesignobjecttoscalar)
|
||||
- [Guzzle\MethodCall](#guzzlemethodcall)
|
||||
- [Jms\Property](#jmsproperty)
|
||||
- [NetteToSymfony](#nettetosymfony)
|
||||
- [PHPStan\Assign](#phpstanassign)
|
||||
- [PHPStan\Cast](#phpstancast)
|
||||
- [PHPUnit](#phpunit)
|
||||
@ -1014,6 +1015,41 @@ Changes properties with `@JMS\DiExtraBundle\Annotation\Inject` to constructor in
|
||||
|
||||
<br>
|
||||
|
||||
## NetteToSymfony
|
||||
|
||||
### `RouterListToControllerAnnotationsRector`
|
||||
|
||||
- class: `Rector\NetteToSymfony\Rector\RouterListToControllerAnnotationsRector`
|
||||
|
||||
Change new Route() from RouteFactory to @Route annotation above controller method
|
||||
|
||||
```diff
|
||||
final class RouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
+
|
||||
+ // case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = new Route('some-path', SomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class SomePresenter
|
||||
{
|
||||
+ /**
|
||||
+ * @Symfony\Component\Routing\Annotation\Route(path="some-path")
|
||||
+ */
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## PHPStan\Assign
|
||||
|
||||
### `PHPStormVarAnnotationRector`
|
||||
|
7
ecs.yml
7
ecs.yml
@ -77,7 +77,6 @@ parameters:
|
||||
- '*/packages/NodeTypeResolver/**/PerNodeTypeResolver/**TypeResolver.php'
|
||||
- '*/packages/NodeTypeResolver/**/PerNodeTypeResolver/**TypeResolver/*Test.php'
|
||||
- '*RectorTest.php'
|
||||
- 'tests/PhpParser/Node/ConstExprEvaluatorFactoryTest.php'
|
||||
- 'src/Rector/AbstractPHPUnitRector.php'
|
||||
- 'src/Rector/Class_/ParentClassToTraitsRector.php'
|
||||
# required for exact string match with "\"
|
||||
@ -106,6 +105,8 @@ parameters:
|
||||
- 'src/PhpParser/Node/Resolver/NameResolver.php'
|
||||
- 'src/Rector/MethodBody/NormalToFluentRector.php'
|
||||
- 'packages/CodingStyle/src/Rector/Use_/RemoveUnusedAliasRector.php'
|
||||
- 'packages/NetteToSymfony/src/Route/RouteInfoFactory.php'
|
||||
|
||||
# copied 3rd party logic
|
||||
- 'packages/Php/src/EregToPcreTransformer.php'
|
||||
# dev
|
||||
@ -140,3 +141,7 @@ parameters:
|
||||
Symplify\CodingStandard\Sniffs\Debug\CommentedOutCodeSniff.Found:
|
||||
# notes
|
||||
- 'packages/Php/src/Rector/Each/ListEachRector.php'
|
||||
|
||||
Symplify\CodingStandard\Sniffs\DependencyInjection\NoClassInstantiationSniff:
|
||||
# 3rd party api
|
||||
- 'src/PhpParser/Node/Value/ValueResolver.php'
|
||||
|
8
packages/NetteToSymfony/config/config.yml
Normal file
8
packages/NetteToSymfony/config/config.yml
Normal file
@ -0,0 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
autowire: true
|
||||
|
||||
Rector\NetteToSymfony\:
|
||||
resource: '../src'
|
||||
exclude: '../src/{Annotation,Route/RouteInfo.php}'
|
@ -0,0 +1,7 @@
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
autowire: true
|
||||
|
||||
Rector\NetteToSymfony\:
|
||||
resource: '../../src'
|
57
packages/NetteToSymfony/src/Annotation/RouteTagValueNode.php
Normal file
57
packages/NetteToSymfony/src/Annotation/RouteTagValueNode.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Annotation;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
|
||||
|
||||
final class RouteTagValueNode implements PhpDocChildNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $routeClass;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $methods = [];
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
*/
|
||||
public function __construct(string $routeClass, string $path, ?string $name = null, array $methods = [])
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->name = $name;
|
||||
$this->routeClass = $routeClass;
|
||||
$this->methods = $methods;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$string = sprintf('@\\%s(', $this->routeClass);
|
||||
|
||||
$string .= sprintf('path="%s"', $this->path);
|
||||
if ($this->name) {
|
||||
$string .= sprintf(', name="%s"', $this->name);
|
||||
}
|
||||
|
||||
if ($this->methods) {
|
||||
$string .= sprintf(', methods={"%s"}', implode('", "', $this->methods));
|
||||
}
|
||||
|
||||
$string .= ')' . PHP_EOL . ' ';
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Rector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\NetteToSymfony\Annotation\RouteTagValueNode;
|
||||
use Rector\NetteToSymfony\Route\RouteInfo;
|
||||
use Rector\NetteToSymfony\Route\RouteInfoFactory;
|
||||
use Rector\NodeTypeResolver\Application\ClassLikeNodeCollector;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockAnalyzer;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\Maintainer\ClassMaintainer;
|
||||
use Rector\PhpParser\Node\Maintainer\ClassMethodMaintainer;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Util\RectorStrings;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* @see https://doc.nette.org/en/2.4/routing
|
||||
* @see https://symfony.com/doc/current/routing.html
|
||||
*/
|
||||
final class RouterListToControllerAnnotationsRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $routeListClass;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $routerClass;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $routeAnnotationClass;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var ClassLikeNodeCollector
|
||||
*/
|
||||
private $classLikeNodeCollector;
|
||||
|
||||
/**
|
||||
* @var ClassMaintainer
|
||||
*/
|
||||
private $classMaintainer;
|
||||
|
||||
/**
|
||||
* @var DocBlockAnalyzer
|
||||
*/
|
||||
private $docBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* @var RouteInfoFactory
|
||||
*/
|
||||
private $routeInfoFactory;
|
||||
|
||||
/**
|
||||
* @var ClassMethodMaintainer
|
||||
*/
|
||||
private $classMethodMaintainer;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
ClassLikeNodeCollector $classLikeNodeCollector,
|
||||
ClassMaintainer $classMaintainer,
|
||||
ClassMethodMaintainer $classMethodMaintainer,
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
RouteInfoFactory $routeInfoFactory,
|
||||
string $routeListClass = 'Nette\Application\Routers\RouteList',
|
||||
string $routerClass = 'Nette\Application\IRouter',
|
||||
string $routeAnnotationClass = 'Symfony\Component\Routing\Annotation\Route'
|
||||
) {
|
||||
$this->routeListClass = $routeListClass;
|
||||
$this->routerClass = $routerClass;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->classLikeNodeCollector = $classLikeNodeCollector;
|
||||
$this->classMaintainer = $classMaintainer;
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->routeAnnotationClass = $routeAnnotationClass;
|
||||
$this->routeInfoFactory = $routeInfoFactory;
|
||||
$this->classMethodMaintainer = $classMethodMaintainer;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Change new Route() from RouteFactory to @Route annotation above controller method',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
final class RouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
$routeList[] = new Route('some-path', SomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
final class RouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
// case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = new Route('some-path', SomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class SomePresenter
|
||||
{
|
||||
/**
|
||||
* @Symfony\Component\Routing\Annotation\Route(path="some-path")
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of nodes this class checks, classes that implement @see \PhpParser\Node
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (empty($node->stmts)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nodeReturnTypes = $this->classMethodMaintainer->resolveReturnType($node);
|
||||
if ($nodeReturnTypes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! in_array($this->routeListClass, $nodeReturnTypes, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assignNodes = $this->resolveAssignRouteNodes($node);
|
||||
if ($assignNodes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$routeInfos = $this->createRouteInfosFromAssignNodes($assignNodes);
|
||||
|
||||
/** @var RouteInfo $routeInfo */
|
||||
foreach ($routeInfos as $routeInfo) {
|
||||
$classMethod = $this->resolveControllerClassMethod($routeInfo);
|
||||
if ($classMethod === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpDocTagNode = new RouteTagValueNode(
|
||||
$this->routeAnnotationClass,
|
||||
$routeInfo->getPath(),
|
||||
null,
|
||||
$routeInfo->getHttpMethods()
|
||||
);
|
||||
|
||||
$this->docBlockAnalyzer->addTag($classMethod, $phpDocTagNode);
|
||||
}
|
||||
|
||||
// complete all other non-explicit methods, from "<presenter>/<action>"
|
||||
$this->completeImplicitRoutes();
|
||||
|
||||
// remove routes
|
||||
$this->removeNodes($assignNodes);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Assign[]
|
||||
*/
|
||||
private function resolveAssignRouteNodes(ClassMethod $node): array
|
||||
{
|
||||
// look for <...>[] = IRoute<Type>
|
||||
return $this->betterNodeFinder->find($node->stmts, function (Node $node) {
|
||||
if (! $node instanceof Assign) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// $routeList[] =
|
||||
if (! $node->var instanceof ArrayDimFetch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isType($node->expr, $this->routerClass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($node->expr instanceof StaticCall) {
|
||||
// for custom static route factories
|
||||
return $this->isRouteStaticCallMatch($node->expr);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Assign[] $assignNodes
|
||||
* @return RouteInfo[]
|
||||
*/
|
||||
private function createRouteInfosFromAssignNodes(array $assignNodes): array
|
||||
{
|
||||
$routeInfos = [];
|
||||
|
||||
// collect annotations and target controllers
|
||||
foreach ($assignNodes as $assignNode) {
|
||||
$routeNameToControllerMethod = $this->routeInfoFactory->createFromNode($assignNode->expr);
|
||||
if ($routeNameToControllerMethod === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routeInfos[] = $routeNameToControllerMethod;
|
||||
}
|
||||
|
||||
return $routeInfos;
|
||||
}
|
||||
|
||||
private function resolveControllerClassMethod(RouteInfo $routeInfo): ?ClassMethod
|
||||
{
|
||||
$classNode = $this->classLikeNodeCollector->findClass($routeInfo->getClass());
|
||||
if ($classNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->classMaintainer->getMethodByName($classNode, $routeInfo->getMethod());
|
||||
}
|
||||
|
||||
private function completeImplicitRoutes(): void
|
||||
{
|
||||
$presenterClassNodes = $this->classLikeNodeCollector->findClassesBySuffix('Presenter');
|
||||
|
||||
foreach ($presenterClassNodes as $presenterClassNode) {
|
||||
foreach ((array) $presenterClassNode->stmts as $classStmt) {
|
||||
if ($this->shouldSkipClassStmt($classStmt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var ClassMethod $classStmt */
|
||||
$path = $this->resolvePathFromClassAndMethodNodes($presenterClassNode, $classStmt);
|
||||
$phpDocTagNode = new RouteTagValueNode($this->routeAnnotationClass, $path);
|
||||
|
||||
$this->docBlockAnalyzer->addTag($classStmt, $phpDocTagNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo allow extension with custom resolvers
|
||||
*/
|
||||
private function isRouteStaticCallMatch(StaticCall $node): bool
|
||||
{
|
||||
$className = $this->getName($node->class);
|
||||
if ($className === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodName = $this->getName($node->name);
|
||||
if ($methodName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @todo decouple - resolve method return type
|
||||
if (! method_exists($className, $methodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodReflection = new ReflectionMethod($className, $methodName);
|
||||
if ($methodReflection->getReturnType()) {
|
||||
$staticCallReturnType = (string) $methodReflection->getReturnType();
|
||||
if (is_a($staticCallReturnType, $this->routerClass, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldSkipClassStmt(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof ClassMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// not an action method
|
||||
if (! $node->isPublic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->matchName($node, '#^(render|action)#')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// already has Route tag
|
||||
return $this->docBlockAnalyzer->hasTag($node, $this->routeAnnotationClass);
|
||||
}
|
||||
|
||||
private function resolvePathFromClassAndMethodNodes(Class_ $classNode, ClassMethod $classMethodNode): string
|
||||
{
|
||||
$presenterName = $this->getName($classNode);
|
||||
$presenterPart = Strings::after($presenterName, '\\', -1);
|
||||
$presenterPart = Strings::substring($presenterPart, 0, -Strings::length('Presenter'));
|
||||
$presenterPart = RectorStrings::camelCaseToDashes($presenterPart);
|
||||
|
||||
$match = Strings::match($this->getName($classMethodNode), '#^(action|render)(?<short_action_name>.*?$)#sm');
|
||||
$actionPart = lcfirst($match['short_action_name']);
|
||||
|
||||
return $presenterPart . '/' . $actionPart;
|
||||
}
|
||||
}
|
76
packages/NetteToSymfony/src/Route/RouteInfo.php
Normal file
76
packages/NetteToSymfony/src/Route/RouteInfo.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Route;
|
||||
|
||||
final class RouteInfo
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $httpMethods = [];
|
||||
|
||||
/**
|
||||
* @param string[] $httpMethods
|
||||
*/
|
||||
public function __construct(
|
||||
string $class,
|
||||
string $method,
|
||||
string $path,
|
||||
?string $name = null,
|
||||
array $httpMethods = []
|
||||
) {
|
||||
$this->class = $class;
|
||||
$this->method = $method;
|
||||
$this->path = $path;
|
||||
$this->name = $name;
|
||||
$this->httpMethods = $httpMethods;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getHttpMethods(): array
|
||||
{
|
||||
return $this->httpMethods;
|
||||
}
|
||||
}
|
157
packages/NetteToSymfony/src/Route/RouteInfoFactory.php
Normal file
157
packages/NetteToSymfony/src/Route/RouteInfoFactory.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Route;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\NodeTypeResolver\Application\ClassLikeNodeCollector;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
|
||||
final class RouteInfoFactory
|
||||
{
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
/**
|
||||
* @var ClassLikeNodeCollector
|
||||
*/
|
||||
private $classLikeNodeCollector;
|
||||
|
||||
public function __construct(
|
||||
NameResolver $nameResolver,
|
||||
ValueResolver $valueResolver,
|
||||
ClassLikeNodeCollector $classLikeNodeCollector
|
||||
) {
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->classLikeNodeCollector = $classLikeNodeCollector;
|
||||
}
|
||||
|
||||
public function createFromNode(Node $expr): ?RouteInfo
|
||||
{
|
||||
if ($expr instanceof New_) {
|
||||
if (! isset($expr->args[0]) || ! isset($expr->args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->createRouteInfoFromArgs($expr);
|
||||
}
|
||||
|
||||
// Route::create()
|
||||
if ($expr instanceof StaticCall) {
|
||||
if (! isset($expr->args[0]) || ! isset($expr->args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = $this->nameResolver->matchNameInsensitiveInMap($expr, [
|
||||
'get' => 'GET',
|
||||
'head' => 'HEAD',
|
||||
'post' => 'POST',
|
||||
'put' => 'PUT',
|
||||
'patch' => 'PATCH',
|
||||
'delete' => 'DELETE',
|
||||
]);
|
||||
|
||||
$methods = [];
|
||||
if ($method !== null) {
|
||||
$methods[] = $method;
|
||||
}
|
||||
|
||||
return $this->createRouteInfoFromArgs($expr, $methods);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param New_|StaticCall $expr
|
||||
* @param string[] $methods
|
||||
*/
|
||||
private function createRouteInfoFromArgs(Node $expr, array $methods = []): ?RouteInfo
|
||||
{
|
||||
$pathArgument = $expr->args[0]->value;
|
||||
$routePath = $this->valueResolver->resolve($pathArgument);
|
||||
|
||||
// route path is needed
|
||||
if ($routePath === null || ! is_string($routePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$targetNode = $expr->args[1]->value;
|
||||
if ($targetNode instanceof ClassConstFetch) {
|
||||
/** @var ClassConstFetch $controllerMethodNode */
|
||||
$controllerMethodNode = $expr->args[1]->value;
|
||||
|
||||
// SomePresenter::class
|
||||
if ($this->nameResolver->isName($controllerMethodNode->name, 'class')) {
|
||||
$presenterClass = $this->nameResolver->resolve($controllerMethodNode->class);
|
||||
if ($presenterClass === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (class_exists($presenterClass) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (method_exists($presenterClass, 'run')) {
|
||||
return new RouteInfo($presenterClass, 'run', $routePath, null, $methods);
|
||||
}
|
||||
}
|
||||
// @todo method specific route
|
||||
}
|
||||
|
||||
if ($targetNode instanceof String_) {
|
||||
$targetValue = $targetNode->value;
|
||||
if (! Strings::contains($targetValue, ':')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$controller, $method] = explode(':', $targetValue);
|
||||
|
||||
// detect class by controller name?
|
||||
// foreach all instance and try to match a name $controller . 'Presenter/Controller'
|
||||
|
||||
$classNode = $this->classLikeNodeCollector->findByShortName($controller . 'Presenter');
|
||||
if ($classNode === null) {
|
||||
$classNode = $this->classLikeNodeCollector->findByShortName($controller . 'Controller');
|
||||
}
|
||||
|
||||
// unable to find here
|
||||
if ($classNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$controllerClass = $this->nameResolver->resolve($classNode);
|
||||
if ($controllerClass === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = null;
|
||||
if (method_exists($controllerClass, 'render' . ucfirst($method))) {
|
||||
$methodName = 'render' . ucfirst($method);
|
||||
} elseif (method_exists($controllerClass, 'action' . ucfirst($method))) {
|
||||
$methodName = 'action' . ucfirst($method);
|
||||
}
|
||||
|
||||
if ($methodName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RouteInfo($controllerClass, $methodName, $routePath, null, []);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class ConstantReferenceRouterFactory
|
||||
{
|
||||
const SOME_PATH = '/some-path';
|
||||
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
// case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = Route::get(self::SOME_PATH, ConstantReferenceSomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class ConstantReferenceSomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class ConstantReferenceRouterFactory
|
||||
{
|
||||
const SOME_PATH = '/some-path';
|
||||
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class ConstantReferenceSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="/some-path", methods={"GET"})
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class GeneralMethodNamedRoutesRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
$routeList[] = new Route('<presenter>/<action>', 'Homepage:default');
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class GeneralMethodNamedRoutesSomePresenter
|
||||
{
|
||||
public function actionFirst()
|
||||
{
|
||||
}
|
||||
|
||||
public function actionSecond()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class GeneralMethodNamedRoutesRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class GeneralMethodNamedRoutesSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="general-method-named-routes-some/first")
|
||||
*/
|
||||
public function actionFirst()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="general-method-named-routes-some/second")
|
||||
*/
|
||||
public function actionSecond()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class MethodNamedRoutesRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
$routeList[] = new Route('hi', 'MethodNamedRoutesSome:first');
|
||||
$routeList[] = new Route('hello', 'MethodNamedRoutesSome:second');
|
||||
$routeList[] = new Route('<presenter>/<action>', 'Homepage:default');
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class MethodNamedRoutesSomePresenter
|
||||
{
|
||||
public function actionFirst()
|
||||
{
|
||||
}
|
||||
|
||||
public function actionSecond()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class MethodNamedRoutesRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class MethodNamedRoutesSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="hi")
|
||||
*/
|
||||
public function actionFirst()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="hello")
|
||||
*/
|
||||
public function actionSecond()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class NewRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
// case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = new Route('some-path', NewSomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class NewSomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class NewRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class NewSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="some-path")
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteFactory;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class StaticRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
// case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = RouteFactory::get('some-path', StaticSomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class StaticSomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Fixture;
|
||||
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteFactory;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
|
||||
final class StaticRouterFactory
|
||||
{
|
||||
public function create(): RouteList
|
||||
{
|
||||
$routeList = new RouteList();
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
}
|
||||
|
||||
final class StaticSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="some-path", methods={"GET"})
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor;
|
||||
|
||||
use Rector\NetteToSymfony\Rector\RouterListToControllerAnnotationsRector;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\Route;
|
||||
use Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source\RouteList;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class RouterListToControllerAnnotationsRetorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/new_route_to_annotation.php.inc',
|
||||
__DIR__ . '/Fixture/static_route_to_annotation.php.inc',
|
||||
__DIR__ . '/Fixture/constant_reference_route_to_annotation.php.inc',
|
||||
__DIR__ . '/Fixture/method_named_routes.php.inc',
|
||||
__DIR__ . '/Fixture/general_method_named_routes.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return RouterListToControllerAnnotationsRector::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getRectorConfiguration(): array
|
||||
{
|
||||
return [
|
||||
'$routeListClass' => RouteList::class,
|
||||
'$routerClass' => Route::class,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source;
|
||||
|
||||
final class Route
|
||||
{
|
||||
public function __construct(string $path, string $target)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source;
|
||||
|
||||
final class RouteFactory
|
||||
{
|
||||
public static function get(string $path, string $presenterClass): Route
|
||||
{
|
||||
return new Route($path, $presenterClass);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source;
|
||||
|
||||
final class RouteList
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NetteToSymfony\Tests\Rector\MethodCall\RouterListToControllerAnnotationsRetor\Source;
|
||||
|
||||
final class SymfonyRouteAnnotation
|
||||
{
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\NodeTypeResolver\Application;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
@ -170,4 +171,33 @@ final class ClassLikeNodeCollector
|
||||
|
||||
return $traits;
|
||||
}
|
||||
|
||||
public function findByShortName(string $shortName): ?Class_
|
||||
{
|
||||
foreach ($this->classes as $className => $classNode) {
|
||||
if (Strings::endsWith($className, '\\' . $shortName)) {
|
||||
return $classNode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Class_[]
|
||||
*/
|
||||
public function findClassesBySuffix(string $suffix): array
|
||||
{
|
||||
$classNodes = [];
|
||||
|
||||
foreach ($this->classes as $className => $classNode) {
|
||||
if (! Strings::endsWith($className, $suffix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classNodes[] = $classNode;
|
||||
}
|
||||
|
||||
return $classNodes;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\NodeTypeResolver\Application;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
@ -38,6 +39,10 @@ final class ConstantNodeCollector
|
||||
|
||||
public function findConstant(string $constantName, string $className): ?ClassConst
|
||||
{
|
||||
if (Strings::contains($constantName, '\\')) {
|
||||
throw new ShouldNotHappenException(sprintf('Switched arguments in "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
return $this->constantsByType[$className][$constantName] ?? null;
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +68,16 @@ final class DocBlockAnalyzer
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize tag name
|
||||
$name = ltrim($name, '@');
|
||||
|
||||
// simple check
|
||||
if (Strings::contains($node->getDocComment()->getText(), '@' . $name)) {
|
||||
if (Strings::contains($node->getDocComment()->getText(), '@' . ltrim($name, '@'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fqn class annotation
|
||||
if (Strings::contains($node->getDocComment()->getText(), '@\\' . ltrim($name, '@'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -100,6 +108,7 @@ final class DocBlockAnalyzer
|
||||
if ($node->getDocComment()) {
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
$phpDocNode->children[] = $phpDocChildNode;
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
} else {
|
||||
$phpDocNode = new PhpDocNode([$phpDocChildNode]);
|
||||
|
@ -26,8 +26,10 @@ parameters:
|
||||
|
||||
ignoreErrors:
|
||||
# false positive
|
||||
- '#Call to function method_exists\(\) with string and (.*?) will always evaluate to false#'
|
||||
- '#PHPDoc tag \@param for parameter \$node with type float is incompatible with native type PhpParser\\Node#'
|
||||
- '#Result of && is always true#'
|
||||
- '#Parameter \#2 \$classMethodNode of method Rector\\NetteToSymfony\\Rector\\RouterListToControllerAnnotationsRector\:\:resolvePathFromClassAndMethodNodes\(\) expects PhpParser\\Node\\Stmt\\ClassMethod, PhpParser\\Node\\Stmt given#'
|
||||
|
||||
# missuse of interface and class
|
||||
- '#Parameter \#1 (.*?) expects Symfony\\Component\\DependencyInjection\\ContainerBuilder, Symfony\\Component\\DependencyInjection\\ContainerInterface given#'
|
||||
@ -103,3 +105,6 @@ parameters:
|
||||
|
||||
# console argument/option
|
||||
- '#Cannot cast array<string\>\|string\|null to string#'
|
||||
|
||||
- '#Parameter \#1 \$nodes of method Rector\\PhpParser\\Node\\BetterNodeFinder\:\:find\(\) expects array<PhpParser\\Node\>\|PhpParser\\Node, array<PhpParser\\Node\\Stmt\>\|null given#'
|
||||
- '#Method Rector\\NetteToSymfony\\Rector\\RouterListToControllerAnnotationsRector\:\:resolveAssignRouteNodes\(\) should return array<PhpParser\\Node\\Expr\\Assign\> but returns array<PhpParser\\Node\>#'
|
||||
|
@ -1,52 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PhpParser\Node;
|
||||
|
||||
use PhpParser\ConstExprEvaluator;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\MagicConst\Dir;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class ConstExprEvaluatorFactory
|
||||
{
|
||||
public function create(): ConstExprEvaluator
|
||||
{
|
||||
return new ConstExprEvaluator(function (Expr $expr): ?string {
|
||||
// resolve "__DIR__"
|
||||
if ($expr instanceof Dir) {
|
||||
$fileInfo = $expr->getAttribute(Attribute::FILE_INFO);
|
||||
if (! $fileInfo instanceof SmartFileInfo) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
return $fileInfo->getPath();
|
||||
}
|
||||
|
||||
// resolve "SomeClass::SOME_CONST"
|
||||
if ($expr instanceof ClassConstFetch) {
|
||||
return $this->resolveClassConstFetch($expr);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private function resolveClassConstFetch(ClassConstFetch $classConstFetchNode): string
|
||||
{
|
||||
$class = $classConstFetchNode->class->getAttribute(Attribute::RESOLVED_NAME);
|
||||
if ($class === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var Identifier $identifierNode */
|
||||
$identifierNode = $classConstFetchNode->name;
|
||||
|
||||
$constant = $identifierNode->toString();
|
||||
|
||||
return $class->toString() . '::' . $constant;
|
||||
}
|
||||
}
|
@ -222,6 +222,21 @@ final class ClassMaintainer
|
||||
return in_array($methodName, $methodNames, true);
|
||||
}
|
||||
|
||||
public function getMethodByName(Class_ $classNode, string $methodName): ?ClassMethod
|
||||
{
|
||||
foreach ($classNode->stmts as $stmt) {
|
||||
if (! $stmt instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->nameResolver->isName($stmt, $methodName)) {
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function tryInsertBeforeFirstMethod(Class_ $classNode, Stmt $node): bool
|
||||
{
|
||||
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||
|
@ -8,7 +8,9 @@ use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Printer\BetterStandardPrinter;
|
||||
|
||||
final class ClassMethodMaintainer
|
||||
@ -23,10 +25,33 @@ final class ClassMethodMaintainer
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
public function __construct(BetterNodeFinder $betterNodeFinder, BetterStandardPrinter $betterStandardPrinter)
|
||||
{
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var FunctionLikeMaintainer
|
||||
*/
|
||||
private $functionLikeMaintainer;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
FunctionLikeMaintainer $functionLikeMaintainer,
|
||||
NameResolver $nameResolver
|
||||
) {
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->functionLikeMaintainer = $functionLikeMaintainer;
|
||||
$this->nameResolver = $nameResolver;
|
||||
}
|
||||
|
||||
public function isParameterUsedMethod(Param $param, ClassMethod $classMethod): bool
|
||||
@ -41,12 +66,12 @@ final class ClassMethodMaintainer
|
||||
public function hasParentMethodOrInterfaceMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
$class = $classMethod->getAttribute(Attribute::CLASS_NAME);
|
||||
if ($class === null) {
|
||||
if (is_string($class) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$method = $classMethod->getAttribute(Attribute::METHOD_NAME);
|
||||
if ($method === null) {
|
||||
if (is_string($method) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -90,6 +115,33 @@ final class ClassMethodMaintainer
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveReturnType(ClassMethod $classMethodNode): array
|
||||
{
|
||||
if ($classMethodNode->returnType !== null) {
|
||||
return $this->nodeTypeResolver->resolve($classMethodNode->returnType);
|
||||
}
|
||||
|
||||
$staticReturnType = $this->functionLikeMaintainer->resolveStaticReturnTypeInfo($classMethodNode);
|
||||
if ($staticReturnType === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$getFqnTypeNode = $staticReturnType->getFqnTypeNode();
|
||||
if ($getFqnTypeNode === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fqnTypeName = $this->nameResolver->resolve($getFqnTypeNode);
|
||||
if ($fqnTypeName === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$fqnTypeName];
|
||||
}
|
||||
|
||||
private function isMethodInParent(string $class, string $method): bool
|
||||
{
|
||||
$parentClass = $class;
|
||||
|
@ -89,6 +89,25 @@ final class NameResolver
|
||||
$this->nameResolversPerNode = $callableCollectorPopulator->populate($resolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $map
|
||||
*/
|
||||
public function matchNameInsensitiveInMap(Node $node, array $map): ?string
|
||||
{
|
||||
foreach ($map as $nameToMatch => $return) {
|
||||
if ($this->isNameInsensitive($node, $nameToMatch)) {
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isNameInsensitive(Node $node, string $name): bool
|
||||
{
|
||||
return strtolower((string) $this->resolve($node)) === strtolower($name);
|
||||
}
|
||||
|
||||
public function isName(Node $node, string $name): bool
|
||||
{
|
||||
$resolvedName = $this->resolve($node);
|
||||
|
125
src/PhpParser/Node/Value/ValueResolver.php
Normal file
125
src/PhpParser/Node/Value/ValueResolver.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PhpParser\Node\Value;
|
||||
|
||||
use PhpParser\ConstExprEvaluator;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Scalar\MagicConst\Dir;
|
||||
use PhpParser\Node\Scalar\MagicConst\File;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Application\ConstantNodeCollector;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class ValueResolver
|
||||
{
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var ConstExprEvaluator
|
||||
*/
|
||||
private $constExprEvaluator;
|
||||
|
||||
/**
|
||||
* @var ConstantNodeCollector
|
||||
*/
|
||||
private $constantNodeCollector;
|
||||
|
||||
public function __construct(NameResolver $nameResolver, ConstantNodeCollector $constantNodeCollector)
|
||||
{
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->constantNodeCollector = $constantNodeCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function resolve(Expr $node)
|
||||
{
|
||||
return $this->getConstExprEvaluator()->evaluateDirectly($node);
|
||||
}
|
||||
|
||||
private function getConstExprEvaluator(): ConstExprEvaluator
|
||||
{
|
||||
if ($this->constExprEvaluator !== null) {
|
||||
return $this->constExprEvaluator;
|
||||
}
|
||||
|
||||
$this->constExprEvaluator = new ConstExprEvaluator(function (Expr $expr): ?string {
|
||||
if ($expr instanceof Dir) {
|
||||
// __DIR__
|
||||
return $this->resolveDirConstant($expr);
|
||||
}
|
||||
|
||||
if ($expr instanceof File) {
|
||||
// __FILE__
|
||||
return $this->resolveFileConstant($expr);
|
||||
}
|
||||
|
||||
// resolve "SomeClass::SOME_CONST"
|
||||
if ($expr instanceof ClassConstFetch) {
|
||||
return $this->resolveClassConstFetch($expr);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $this->constExprEvaluator;
|
||||
}
|
||||
|
||||
private function resolveDirConstant(Dir $dirNode): string
|
||||
{
|
||||
$fileInfo = $dirNode->getAttribute(Attribute::FILE_INFO);
|
||||
if (! $fileInfo instanceof SmartFileInfo) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
return $fileInfo->getPath();
|
||||
}
|
||||
|
||||
private function resolveFileConstant(File $fileNode): string
|
||||
{
|
||||
$fileInfo = $fileNode->getAttribute(Attribute::FILE_INFO);
|
||||
if (! $fileInfo instanceof SmartFileInfo) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
return $fileInfo->getPathname();
|
||||
}
|
||||
|
||||
private function resolveClassConstFetch(ClassConstFetch $classConstFetchNode): string
|
||||
{
|
||||
$class = $this->nameResolver->resolve($classConstFetchNode->class);
|
||||
$constant = $this->nameResolver->resolve($classConstFetchNode->name);
|
||||
|
||||
if ($class === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($constant === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($class === 'self') {
|
||||
$class = (string) $classConstFetchNode->class->getAttribute(Attribute::CLASS_NAME);
|
||||
}
|
||||
|
||||
if ($constant === 'class') {
|
||||
return $class;
|
||||
}
|
||||
|
||||
$classConstNode = $this->constantNodeCollector->findConstant($constant, $class);
|
||||
|
||||
if ($classConstNode === null) {
|
||||
// fallback to the name
|
||||
return $class . '::' . $constant;
|
||||
}
|
||||
|
||||
return $this->constExprEvaluator->evaluateDirectly($classConstNode->consts[0]->value);
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace Rector\Rector;
|
||||
|
||||
use PhpParser\ConstExprEvaluator;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Stmt;
|
||||
@ -13,6 +12,7 @@ use Rector\Application\RemovedFilesCollector;
|
||||
use Rector\Contract\Rector\PhpRectorInterface;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
@ -35,28 +35,28 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
*/
|
||||
private $symfonyStyle;
|
||||
|
||||
/**
|
||||
* @var ConstExprEvaluator
|
||||
*/
|
||||
private $constExprEvaluator;
|
||||
|
||||
/**
|
||||
* @var RemovedFilesCollector
|
||||
*/
|
||||
private $removedFilesCollector;
|
||||
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setAbstractRectorDependencies(
|
||||
AppliedRectorCollector $appliedRectorCollector,
|
||||
SymfonyStyle $symfonyStyle,
|
||||
ConstExprEvaluator $constExprEvaluator,
|
||||
ValueResolver $valueResolver,
|
||||
RemovedFilesCollector $removedFilesCollector
|
||||
): void {
|
||||
$this->appliedRectorCollector = $appliedRectorCollector;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->constExprEvaluator = $constExprEvaluator;
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->removedFilesCollector = $removedFilesCollector;
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
*/
|
||||
protected function getValue(Expr $node)
|
||||
{
|
||||
return $this->constExprEvaluator->evaluateSilently($node);
|
||||
return $this->valueResolver->resolve($node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\Rector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
|
||||
@ -34,9 +35,22 @@ trait NameResolverTrait
|
||||
return $this->getName($firstNode) === $this->getName($secondNode);
|
||||
}
|
||||
|
||||
public function matchName(Node $node, string $pattern): bool
|
||||
{
|
||||
return (bool) Strings::match($this->getName($node), $pattern);
|
||||
}
|
||||
|
||||
public function isNameInsensitive(Node $node, string $name): bool
|
||||
{
|
||||
return strtolower((string) $this->getName($node)) === strtolower($name);
|
||||
return $this->nameResolver->isNameInsensitive($node, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $map
|
||||
*/
|
||||
public function matchNameInsensitiveInMap(Node $node, array $map): ?string
|
||||
{
|
||||
return $this->nameResolver->matchNameInsensitiveInMap($node, $map);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\Util;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Symfony\Component\Console\Input\StringInput;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
|
||||
@ -16,4 +17,26 @@ final class RectorStrings
|
||||
|
||||
return $privatesCaller->callPrivateMethod(new StringInput(''), 'tokenize', $command);
|
||||
}
|
||||
|
||||
public static function camelCaseToDashes(string $input): string
|
||||
{
|
||||
return self::camelCaseToGlue($input, '-');
|
||||
}
|
||||
|
||||
public static function camelCaseToUnderscore(string $input): string
|
||||
{
|
||||
return self::camelCaseToGlue($input, '_');
|
||||
}
|
||||
|
||||
private static function camelCaseToGlue(string $input, string $glue): string
|
||||
{
|
||||
$matches = Strings::matchAll($input, '!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!');
|
||||
|
||||
$parts = [];
|
||||
foreach ($matches as $match) {
|
||||
$parts[] = $match[0] === strtoupper($match[0]) ? strtolower($match[0]) : lcfirst($match[0]);
|
||||
}
|
||||
|
||||
return implode($glue, $parts);
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,6 @@ services:
|
||||
PhpParser\NodeFinder: ~
|
||||
|
||||
# value resolver
|
||||
PhpParser\ConstExprEvaluator:
|
||||
factory: ['@Rector\PhpParser\Node\ConstExprEvaluatorFactory', 'create']
|
||||
|
||||
Symfony\Component\Filesystem\Filesystem: ~
|
||||
|
||||
# Symfony\Console
|
||||
|
@ -1,34 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\PhpParser\Node;
|
||||
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\ConstExprEvaluator;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\ConstExprEvaluatorFactory;
|
||||
|
||||
final class ConstExprEvaluatorFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ConstExprEvaluator
|
||||
*/
|
||||
private $constExprEvaluator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->constExprEvaluator = (new ConstExprEvaluatorFactory())->create();
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$classConstFetchNode = (new BuilderFactory())->classConstFetch('SomeClass', 'SOME_CONSTANT');
|
||||
$classConstFetchNode->class->setAttribute(Attribute::RESOLVED_NAME, new Name('SomeClassResolveName'));
|
||||
|
||||
$this->assertSame(
|
||||
'SomeClassResolveName::SOME_CONSTANT',
|
||||
$this->constExprEvaluator->evaluateDirectly($classConstFetchNode)
|
||||
);
|
||||
}
|
||||
}
|
33
tests/PhpParser/Node/Value/ValueResolverTest.php
Normal file
33
tests/PhpParser/Node/Value/ValueResolverTest.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\PhpParser\Node\Value;
|
||||
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
final class ValueResolverTest extends AbstractContainerAwareTestCase
|
||||
{
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->valueResolver = $this->container->get(ValueResolver::class);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$classConstFetchNode = (new BuilderFactory())->classConstFetch('SomeClass', 'SOME_CONSTANT');
|
||||
$classConstFetchNode->class->setAttribute(Attribute::RESOLVED_NAME, new FullyQualified('SomeClassResolveName'));
|
||||
|
||||
$this->assertSame(
|
||||
'SomeClassResolveName::SOME_CONSTANT',
|
||||
$this->valueResolver->resolve($classConstFetchNode)
|
||||
);
|
||||
}
|
||||
}
|
@ -3,13 +3,14 @@
|
||||
namespace ChangeMe_ToNamespaced;
|
||||
|
||||
use PHPUnit_TestCase;
|
||||
use Rector\Tests\Rector\Namespace_\PseudoNamespaceToNamespaceRector\Source\Keep_This;
|
||||
|
||||
class SomeTestCase
|
||||
{
|
||||
/**
|
||||
* @return \ChangeMe_AnotherNamespace
|
||||
*/
|
||||
public function someMethod(): \Keep_ThisThough
|
||||
public function someMethod(): Keep_This
|
||||
{
|
||||
if ($this instanceof PHPUnit_TestCase) {
|
||||
return true;
|
||||
@ -24,13 +25,14 @@ class SomeTestCase
|
||||
namespace ChangeMe\ToNamespaced;
|
||||
|
||||
use PHPUnit\TestCase;
|
||||
use Rector\Tests\Rector\Namespace_\PseudoNamespaceToNamespaceRector\Source\Keep_This;
|
||||
|
||||
class SomeTestCase
|
||||
{
|
||||
/**
|
||||
* @return \ChangeMe_AnotherNamespace
|
||||
*/
|
||||
public function someMethod(): \Keep_ThisThough
|
||||
public function someMethod(): Keep_This
|
||||
{
|
||||
if ($this instanceof \PHPUnit\TestCase) {
|
||||
return true;
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Namespace_\PseudoNamespaceToNamespaceRector\Source;
|
||||
|
||||
final class Keep_This
|
||||
{
|
||||
|
||||
}
|
31
tests/Util/RectorStringsTest.php
Normal file
31
tests/Util/RectorStringsTest.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Util;
|
||||
|
||||
use Iterator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Rector\Util\RectorStrings;
|
||||
|
||||
final class RectorStringsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDataForCamelCaseToUnderscore()
|
||||
*/
|
||||
public function testCamelCaseToUnderscore(string $content, string $expected): void
|
||||
{
|
||||
$this->assertSame($expected, RectorStrings::camelCaseToUnderscore($content));
|
||||
}
|
||||
|
||||
public function provideDataForCamelCaseToUnderscore(): Iterator
|
||||
{
|
||||
yield ['simpleTest', 'simple_test'];
|
||||
yield ['easy', 'easy'];
|
||||
yield ['HTML', 'html'];
|
||||
yield ['simpleXML', 'simple_xml'];
|
||||
yield ['PDFLoad', 'pdf_load'];
|
||||
yield ['startMIDDLELast', 'start_middle_last'];
|
||||
yield ['AString', 'a_string'];
|
||||
yield ['Some4Numbers234', 'some4_numbers234'];
|
||||
yield ['TEST123String', 'test123_string'];
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
services:
|
||||
- Rector\PHPStanExtensions\Utils\ValueResolver
|
||||
- Rector\PHPStanExtensions\Utils\PHPStanValueResolver
|
||||
|
||||
# $node->geAttribute($1) => Type|null by $1
|
||||
- { class: Rector\PHPStanExtensions\Rector\Type\GetAttributeReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
|
@ -22,7 +22,7 @@ use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStanExtensions\Utils\ValueResolver;
|
||||
use Rector\PHPStanExtensions\Utils\PHPStanValueResolver;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeExtension
|
||||
@ -52,13 +52,13 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
|
||||
];
|
||||
|
||||
/**
|
||||
* @var ValueResolver
|
||||
* @var PHPStanValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
private $phpStanValueResolver;
|
||||
|
||||
public function __construct(ValueResolver $valueResolver)
|
||||
public function __construct(PHPStanValueResolver $phpStanValueResolver)
|
||||
{
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->phpStanValueResolver = $phpStanValueResolver;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
@ -107,7 +107,7 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
|
||||
private function resolveArgumentValue(Expr $node): ?string
|
||||
{
|
||||
if ($node instanceof ClassConstFetch) {
|
||||
return $this->valueResolver->resolveClassConstFetch($node);
|
||||
return $this->phpStanValueResolver->resolveClassConstFetch($node);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -6,7 +6,7 @@ use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
|
||||
final class ValueResolver
|
||||
final class PHPStanValueResolver
|
||||
{
|
||||
public function resolveClassConstFetch(ClassConstFetch $classConstFetch): ?string
|
||||
{
|
Loading…
x
Reference in New Issue
Block a user