mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 11:50:51 +00:00
[Decouple] Remove set, rather job for PHPStorm (#4521)
* [Decouple] Remove set, rather PHPStorm job * update docs
This commit is contained in:
parent
18d2ea2d31
commit
9751b9a1e8
|
@ -98,7 +98,6 @@
|
|||
"Rector\\ConsoleDiffer\\": "packages/console-differ/src",
|
||||
"Rector\\Core\\": "src",
|
||||
"Rector\\DeadCode\\": "rules/dead-code/src",
|
||||
"Rector\\Decouple\\": "rules/decouple/src",
|
||||
"Rector\\DoctrineAnnotationGenerated\\": "packages/doctrine-annotation-generated/src",
|
||||
"Rector\\DoctrineCodeQuality\\": "rules/doctrine-code-quality/src",
|
||||
"Rector\\DoctrineGedmoToKnplabs\\": "rules/doctrine-gedmo-to-knplabs/src",
|
||||
|
@ -227,7 +226,6 @@
|
|||
"Rector\\Compiler\\Tests\\": "compiler/tests",
|
||||
"Rector\\Core\\Tests\\": "tests",
|
||||
"Rector\\DeadCode\\Tests\\": "rules/dead-code/tests",
|
||||
"Rector\\Decouple\\Tests\\": "rules/decouple/tests",
|
||||
"Rector\\DoctrineCodeQuality\\Tests\\": "rules/doctrine-code-quality/tests",
|
||||
"Rector\\DoctrineGedmoToKnplabs\\Tests\\": "rules/doctrine-gedmo-to-knplabs/tests",
|
||||
"Rector\\Doctrine\\Tests\\": "rules/doctrine/tests",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# All 600 Rectors Overview
|
||||
# All 599 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
---
|
||||
|
@ -11,7 +11,6 @@
|
|||
- [CodeQuality](#codequality) (61)
|
||||
- [CodingStyle](#codingstyle) (33)
|
||||
- [DeadCode](#deadcode) (41)
|
||||
- [Decouple](#decouple) (1)
|
||||
- [Defluent](#defluent) (8)
|
||||
- [Doctrine](#doctrine) (17)
|
||||
- [DoctrineCodeQuality](#doctrinecodequality) (9)
|
||||
|
@ -3479,76 +3478,6 @@ Change ternary of bool : false to && bool
|
|||
|
||||
<br><br>
|
||||
|
||||
## Decouple
|
||||
|
||||
### `DecoupleClassMethodToOwnClassRector`
|
||||
|
||||
- class: [`Rector\Decouple\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector`](/rules/decouple/src/Rector/ClassMethod/DecoupleClassMethodToOwnClassRector.php)
|
||||
- [test fixtures](/rules/decouple/tests/Rector/ClassMethod/DecoupleClassMethodToOwnClassRector/Fixture)
|
||||
|
||||
Move class method with its all dependencies to own class by method name
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Decouple\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(DecoupleClassMethodToOwnClassRector::class)
|
||||
->call('configure', [[
|
||||
DecoupleClassMethodToOwnClassRector::METHOD_NAMES_BY_CLASS => [
|
||||
'SomeClass' => [
|
||||
'someMethod' => [
|
||||
'class' => 'NewDecoupledClass',
|
||||
'method' => 'someRenamedMethod',
|
||||
'parent_class' => 'AddedParentClass',
|
||||
],
|
||||
],
|
||||
],
|
||||
]]);
|
||||
};
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
- public function someMethod()
|
||||
- {
|
||||
- $this->alsoCallThis();
|
||||
- }
|
||||
-
|
||||
- private function alsoCallThis()
|
||||
- {
|
||||
- }
|
||||
}
|
||||
```
|
||||
|
||||
**New file**
|
||||
```php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
class NewDecoupledClass extends AddedParentClass
|
||||
{
|
||||
public function someRenamedMethod(): void
|
||||
{
|
||||
$this->alsoCallThis();
|
||||
}
|
||||
|
||||
private function alsoCallThis(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
## Defluent
|
||||
|
||||
### `DefluentReturnMethodCallRector`
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->defaults()
|
||||
->autowire()
|
||||
->public()
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\Decouple\\', __DIR__ . '/../src')
|
||||
->exclude([__DIR__ . '/../src/Rector', __DIR__ . '/../src/ValueObject']);
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Matcher;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Decouple\ValueObject\DecoupleClassMethodMatch;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class DecoupledClassMethodMatcher
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, NodeTypeResolver $nodeTypeResolver)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<mixed>> $methodNamesByClass
|
||||
*/
|
||||
public function matchDecoupled(ClassMethod $classMethod, array $methodNamesByClass): ?DecoupleClassMethodMatch
|
||||
{
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classLike === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($methodNamesByClass as $className => $configuration) {
|
||||
if (! $this->nodeTypeResolver->isObjectType($classLike, $className)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($configuration as $methodName => $newClassConfiguration) {
|
||||
if (! $this->nodeNameResolver->isName($classMethod->name, $methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return new DecoupleClassMethodMatch(
|
||||
$newClassConfiguration['class'],
|
||||
$newClassConfiguration['method'],
|
||||
$newClassConfiguration['parent_class'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\NodeFactory;
|
||||
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Core\PhpParser\Builder\MethodBuilder;
|
||||
use Rector\Core\PhpParser\Builder\ParamBuilder;
|
||||
use Rector\Core\ValueObject\MethodName;
|
||||
|
||||
final class ConstructorClassMethodFactory
|
||||
{
|
||||
/**
|
||||
* @param array<string, Property> $properties
|
||||
*/
|
||||
public function create(array $properties): ?ClassMethod
|
||||
{
|
||||
if ($properties === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodBuilder = new MethodBuilder(MethodName::CONSTRUCT);
|
||||
$methodBuilder->makePublic();
|
||||
|
||||
foreach ($properties as $propertyName => $property) {
|
||||
/** @var string $propertyName */
|
||||
$paramBuilder = new ParamBuilder($propertyName);
|
||||
|
||||
/** @var Property $property */
|
||||
if ($property->type !== null) {
|
||||
$paramBuilder->setType($property->type);
|
||||
}
|
||||
|
||||
$methodBuilder->addParam($paramBuilder->getNode());
|
||||
|
||||
// add assign
|
||||
$assign = $this->createAssign($propertyName);
|
||||
|
||||
$methodBuilder->addStmt($assign);
|
||||
}
|
||||
|
||||
return $methodBuilder->getNode();
|
||||
}
|
||||
|
||||
private function createAssign(string $propertyName): Assign
|
||||
{
|
||||
$localPropertyFetch = new PropertyFetch(new Variable('this'), $propertyName);
|
||||
|
||||
return new Assign($localPropertyFetch, new Variable($propertyName));
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\NodeFactory;
|
||||
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Builder\ClassBuilder;
|
||||
use Rector\Decouple\ValueObject\DecoupleClassMethodMatch;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class NamespaceFactory
|
||||
{
|
||||
/**
|
||||
* @param Stmt[] $classStmts
|
||||
*/
|
||||
public function createNamespacedClassByNameAndStmts(
|
||||
Class_ $class,
|
||||
DecoupleClassMethodMatch $decoupleClassMethodMatch,
|
||||
array $classStmts
|
||||
): Namespace_ {
|
||||
/** @var Namespace_|null $namespace */
|
||||
$namespace = $class->getAttribute(AttributeKey::NAMESPACE_NODE);
|
||||
if ($namespace === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
foreach ($namespace->stmts as $key => $stmt) {
|
||||
if (! $stmt instanceof Class_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$namespace->stmts[$key] = $this->createNewClass($decoupleClassMethodMatch, $classStmts);
|
||||
}
|
||||
|
||||
$this->createNewClass($decoupleClassMethodMatch, $classStmts);
|
||||
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Stmt[] $classStmts
|
||||
*/
|
||||
private function createNewClass(DecoupleClassMethodMatch $decoupleClassMethodMatch, array $classStmts): Class_
|
||||
{
|
||||
$classBuilder = new ClassBuilder($decoupleClassMethodMatch->getClassName());
|
||||
$classBuilder->addStmts($classStmts);
|
||||
$classBuilder->makeFinal();
|
||||
|
||||
$parentClassName = $decoupleClassMethodMatch->getParentClassName();
|
||||
if ($parentClassName !== null) {
|
||||
$classBuilder->extend(new FullyQualified($parentClassName));
|
||||
}
|
||||
|
||||
return $classBuilder->getNode();
|
||||
}
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Decouple\Matcher\DecoupledClassMethodMatcher;
|
||||
use Rector\Decouple\NodeFactory\ConstructorClassMethodFactory;
|
||||
use Rector\Decouple\NodeFactory\NamespaceFactory;
|
||||
use Rector\Decouple\UsedNodesExtractor\UsedClassConstsExtractor;
|
||||
use Rector\Decouple\UsedNodesExtractor\UsedClassMethodsExtractor;
|
||||
use Rector\Decouple\UsedNodesExtractor\UsedClassPropertyExtractor;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://amateri.com for sponsoring this rule - visit them on https://www.startupjobs.cz/startup/scrumworks-s-r-o
|
||||
*
|
||||
* @see \Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\DecoupleClassMethodToOwnClassRectorTest
|
||||
*/
|
||||
final class DecoupleClassMethodToOwnClassRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const METHOD_NAMES_BY_CLASS = '$methodNamesByClass';
|
||||
|
||||
/**
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
private $methodNamesByClass = [];
|
||||
|
||||
/**
|
||||
* @var NamespaceFactory
|
||||
*/
|
||||
private $namespaceFactory;
|
||||
|
||||
/**
|
||||
* @var UsedClassMethodsExtractor
|
||||
*/
|
||||
private $usedClassMethodsExtractor;
|
||||
|
||||
/**
|
||||
* @var UsedClassConstsExtractor
|
||||
*/
|
||||
private $usedClassConstsExtractor;
|
||||
|
||||
/**
|
||||
* @var UsedClassPropertyExtractor
|
||||
*/
|
||||
private $usedClassPropertyExtractor;
|
||||
|
||||
/**
|
||||
* @var DecoupledClassMethodMatcher
|
||||
*/
|
||||
private $decoupledClassMethodMatcher;
|
||||
|
||||
/**
|
||||
* @var ConstructorClassMethodFactory
|
||||
*/
|
||||
private $constructorClassMethodFactory;
|
||||
|
||||
public function __construct(
|
||||
NamespaceFactory $namespaceFactory,
|
||||
UsedClassMethodsExtractor $usedClassMethodsExtractor,
|
||||
UsedClassConstsExtractor $usedClassConstsExtractor,
|
||||
UsedClassPropertyExtractor $usedClassPropertyExtractor,
|
||||
DecoupledClassMethodMatcher $decoupledClassMethodMatcher,
|
||||
ConstructorClassMethodFactory $constructorClassMethodFactory
|
||||
) {
|
||||
$this->namespaceFactory = $namespaceFactory;
|
||||
$this->usedClassMethodsExtractor = $usedClassMethodsExtractor;
|
||||
$this->usedClassConstsExtractor = $usedClassConstsExtractor;
|
||||
$this->usedClassPropertyExtractor = $usedClassPropertyExtractor;
|
||||
$this->decoupledClassMethodMatcher = $decoupledClassMethodMatcher;
|
||||
$this->constructorClassMethodFactory = $constructorClassMethodFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$decoupleClassMethodMatch = $this->decoupledClassMethodMatcher->matchDecoupled(
|
||||
$node,
|
||||
$this->methodNamesByClass
|
||||
);
|
||||
if ($decoupleClassMethodMatch === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$mainClassMethod = clone $node;
|
||||
$mainClassMethod->name = new Identifier($decoupleClassMethodMatch->getMethodName());
|
||||
$this->makePublic($mainClassMethod);
|
||||
|
||||
// 2. get related class constants in the same class
|
||||
$usedClassConsts = $this->usedClassConstsExtractor->extract($node);
|
||||
|
||||
// 3. get class method related methods call in the same class
|
||||
$usedClassMethods = $this->usedClassMethodsExtractor->extractFromClassMethod(
|
||||
$node,
|
||||
$decoupleClassMethodMatch->getParentClassName()
|
||||
);
|
||||
|
||||
// 4. get class method related property fetches in the same class - add to constructor
|
||||
$classMethods = array_merge($usedClassMethods, [$node]);
|
||||
$usedProperties = $this->usedClassPropertyExtractor->extractFromClassMethods(
|
||||
$classMethods,
|
||||
$decoupleClassMethodMatch->getParentClassName()
|
||||
);
|
||||
|
||||
// 5. add constructor dependencies $requiredLocalPropertyFetches
|
||||
/** @var Class_ $classLike */
|
||||
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
$constructClassMethod = $this->constructorClassMethodFactory->create($usedProperties);
|
||||
|
||||
// 6. build a class
|
||||
$usedClassStmts = array_merge(
|
||||
$usedClassConsts,
|
||||
$usedProperties,
|
||||
$constructClassMethod !== null ? [$constructClassMethod] : [],
|
||||
[$mainClassMethod],
|
||||
$usedClassMethods
|
||||
);
|
||||
|
||||
$namespace = $this->namespaceFactory->createNamespacedClassByNameAndStmts(
|
||||
$classLike,
|
||||
$decoupleClassMethodMatch,
|
||||
$usedClassStmts
|
||||
);
|
||||
|
||||
$newClassLocation = $this->createNewClassLocation($node, $decoupleClassMethodMatch->getClassName());
|
||||
$this->printNodesToFilePath([$namespace], $newClassLocation);
|
||||
|
||||
// 7. cleanup this class method
|
||||
$this->removeNode($node);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Move class method with its all dependencies to own class by method name', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function someMethod()
|
||||
{
|
||||
$this->alsoCallThis();
|
||||
}
|
||||
|
||||
private function alsoCallThis()
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
self::METHOD_NAMES_BY_CLASS => [
|
||||
'SomeClass' => [
|
||||
'someMethod' => [
|
||||
'class' => 'NewDecoupledClass',
|
||||
'method' => 'someRenamedMethod',
|
||||
'parent_class' => 'AddedParentClass',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
<<<'CODE_SAMPLE'
|
||||
<?php
|
||||
|
||||
class NewDecoupledClass extends AddedParentClass
|
||||
{
|
||||
public function someRenamedMethod()
|
||||
{
|
||||
$this->alsoCallThis();
|
||||
}
|
||||
|
||||
private function alsoCallThis()
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->methodNamesByClass = $configuration[self::METHOD_NAMES_BY_CLASS] ?? [];
|
||||
}
|
||||
|
||||
private function createNewClassLocation(ClassMethod $classMethod, string $newClassName): string
|
||||
{
|
||||
/** @var SmartFileInfo|null $fileInfo */
|
||||
$fileInfo = $classMethod->getAttribute(AttributeKey::FILE_INFO);
|
||||
if ($fileInfo === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
return $fileInfo->getPath() . DIRECTORY_SEPARATOR . $newClassName . '.php';
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\UsedNodesExtractor;
|
||||
|
||||
use function implode;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use function sprintf;
|
||||
|
||||
final class UsedClassConstsExtractor
|
||||
{
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassConst[]
|
||||
*/
|
||||
public function extract(ClassMethod $classMethod): array
|
||||
{
|
||||
$classConsts = [];
|
||||
|
||||
/** @var Class_ $classLike */
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
&$classConsts,
|
||||
$classLike
|
||||
) {
|
||||
if (! $node instanceof ClassConstFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isName($node->class, 'self')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classConstName = $this->nodeNameResolver->getName($node->name);
|
||||
if ($classConstName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classConsts[$classConstName] = $this->getClassConstByName($classLike, $classConstName);
|
||||
});
|
||||
|
||||
return $classConsts;
|
||||
}
|
||||
|
||||
private function getClassConstByName(Class_ $class, string $classConstName): ClassConst
|
||||
{
|
||||
$classConstantNames = [];
|
||||
foreach ($class->getConstants() as $classConst) {
|
||||
$classConstantNames[] = $this->nodeNameResolver->getName($classConst);
|
||||
|
||||
if (! $this->nodeNameResolver->isName($classConst, $classConstName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $classConst;
|
||||
}
|
||||
|
||||
$className = $this->nodeNameResolver->getName($class);
|
||||
$message = sprintf(
|
||||
'Cannot find "%s::%s" constant. Pick one of: %s',
|
||||
$className,
|
||||
$classConstName,
|
||||
implode(', ', $classConstantNames)
|
||||
);
|
||||
|
||||
throw new ShouldNotHappenException($message);
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\UsedNodesExtractor;
|
||||
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function in_array;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
|
||||
final class UsedClassMethodsExtractor
|
||||
{
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMethod[]
|
||||
*/
|
||||
public function extractFromClassMethod(ClassMethod $classMethod, ?string $parentClassName = null): array
|
||||
{
|
||||
/** @var Class_ $classLike */
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
|
||||
$classMethods = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
&$classMethods,
|
||||
$classLike
|
||||
) {
|
||||
if (! $node instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isThisPropertyFetch($node->var)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($node->name);
|
||||
|
||||
$classMethod = $classLike->getMethod($methodName);
|
||||
if ($classMethod === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$classMethods[$methodName] = $classMethod;
|
||||
});
|
||||
|
||||
// 2nd nesting method calls
|
||||
foreach ($classMethods as $nestedClassMethod) {
|
||||
$classMethods = array_merge($classMethods, $this->extractFromClassMethod($nestedClassMethod));
|
||||
}
|
||||
|
||||
$uniqueClassMethods = $this->makeClassesMethodsUnique($classMethods);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
return $this->filterOutParentClassMethods($uniqueClassMethods, $parentClassName);
|
||||
}
|
||||
|
||||
return $uniqueClassMethods;
|
||||
}
|
||||
|
||||
private function isThisPropertyFetch(Node $node): bool
|
||||
{
|
||||
if ($node instanceof MethodCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($node instanceof StaticCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->nodeNameResolver->isName($node, 'this');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod[] $classMethods
|
||||
* @return ClassMethod[]
|
||||
*/
|
||||
private function makeClassesMethodsUnique(array $classMethods): array
|
||||
{
|
||||
$uniqueClassMethods = [];
|
||||
|
||||
foreach ($classMethods as $classMethod) {
|
||||
/** @var string $classMethodName */
|
||||
$classMethodName = $this->nodeNameResolver->getName($classMethod);
|
||||
$uniqueClassMethods[$classMethodName] = $classMethod;
|
||||
}
|
||||
|
||||
return $uniqueClassMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod[] $classMethods
|
||||
* @return ClassMethod[]
|
||||
*/
|
||||
private function filterOutParentClassMethods(array $classMethods, string $parentClassName): array
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($parentClassName);
|
||||
$parentClassMethodNames = [];
|
||||
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
|
||||
$parentClassMethodNames[] = $reflectionMethod->getName();
|
||||
}
|
||||
|
||||
foreach (array_keys($classMethods) as $methodName) {
|
||||
if (! in_array($methodName, $parentClassMethodNames, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($classMethods[$methodName]);
|
||||
}
|
||||
|
||||
return $classMethods;
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\UsedNodesExtractor;
|
||||
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function in_array;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
|
||||
final class UsedClassPropertyExtractor
|
||||
{
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod[] $classMethods
|
||||
* @return Property[] <int|string, \PhpParser\Node\Stmt\Property>
|
||||
*/
|
||||
public function extractFromClassMethods(array $classMethods, ?string $parentClassName = null): array
|
||||
{
|
||||
$properties = [];
|
||||
|
||||
foreach ($classMethods as $classMethod) {
|
||||
$properties = array_merge($properties, $this->extract($classMethod));
|
||||
}
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
return $this->filterOutParentClassProperties($properties, $parentClassName);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Property[]
|
||||
*/
|
||||
private function extract(ClassMethod $classMethod): array
|
||||
{
|
||||
$properties = [];
|
||||
|
||||
/** @var Class_ $classLike */
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
&$properties,
|
||||
$classLike
|
||||
) {
|
||||
if (! $node instanceof PropertyFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isThisPropertyFetch($node->var)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($node->name);
|
||||
if ($propertyName === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
/** @var Property|null $property */
|
||||
$property = $classLike->getProperty($propertyName);
|
||||
if ($property === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$properties[$propertyName] = $property;
|
||||
});
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property[] $classProperties
|
||||
* @return Property[]
|
||||
*/
|
||||
private function filterOutParentClassProperties(array $classProperties, string $parentClassName): array
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($parentClassName);
|
||||
|
||||
$parentClassPropertyNames = [];
|
||||
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
|
||||
$parentClassPropertyNames[] = $reflectionProperty->getName();
|
||||
}
|
||||
|
||||
foreach (array_keys($classProperties) as $propertyName) {
|
||||
if (! in_array($propertyName, $parentClassPropertyNames, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($classProperties[$propertyName]);
|
||||
}
|
||||
|
||||
return $classProperties;
|
||||
}
|
||||
|
||||
private function isThisPropertyFetch(Node $node): bool
|
||||
{
|
||||
if ($node instanceof MethodCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($node instanceof StaticCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->nodeNameResolver->isName($node, 'this');
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\ValueObject;
|
||||
|
||||
final class DecoupleClassMethodMatch
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $methodName;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $parentClassName;
|
||||
|
||||
public function __construct(string $className, string $methodName, ?string $parentClassName = null)
|
||||
{
|
||||
$this->className = $className;
|
||||
$this->parentClassName = $parentClassName;
|
||||
$this->methodName = $methodName;
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
public function getParentClassName(): ?string
|
||||
{
|
||||
return $this->parentClassName;
|
||||
}
|
||||
|
||||
public function getMethodName(): string
|
||||
{
|
||||
return $this->methodName;
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Decouple\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector;
|
||||
use Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source\AbstractFather;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class DecoupleClassMethodToOwnClassRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo, string $expectedFilePath, string $expectedContentFilePath): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
|
||||
$this->assertFileExists($expectedFilePath);
|
||||
$this->assertFileEquals($expectedContentFilePath, $expectedFilePath);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Fixture/basic.php.inc'),
|
||||
// expected new file location
|
||||
$this->getTempPath() . '/ExtraClassName.php',
|
||||
// expected new file content
|
||||
__DIR__ . '/Source/ExpectedExtraClassName.php',
|
||||
];
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Fixture/with_property_dependency.php.inc'),
|
||||
// expected new file location
|
||||
$this->getTempPath() . '/ExtraUsingProperty.php',
|
||||
// expected new file content
|
||||
__DIR__ . '/Source/ExpectedExtraUsingProperty.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed[]>
|
||||
*/
|
||||
protected function getRectorsWithConfiguration(): array
|
||||
{
|
||||
return [
|
||||
DecoupleClassMethodToOwnClassRector::class => [
|
||||
DecoupleClassMethodToOwnClassRector::METHOD_NAMES_BY_CLASS => [
|
||||
'Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture\Basic' => [
|
||||
'someMethod' => [
|
||||
'method' => 'newMethodName',
|
||||
'class' => 'ExtraClassName',
|
||||
// optionally: "parent_class" =>
|
||||
],
|
||||
],
|
||||
'Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture\WithPropertyDependency' => [
|
||||
'usingProperty' => [
|
||||
'method' => 'newUsingProperty',
|
||||
'class' => 'ExtraUsingProperty',
|
||||
'parent_class' => AbstractFather::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
final class Basic
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const VALUE = 'value';
|
||||
|
||||
public function another()
|
||||
{
|
||||
return $this->someMethod();
|
||||
}
|
||||
|
||||
private function someMethod()
|
||||
{
|
||||
return self::VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
final class Basic
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const VALUE = 'value';
|
||||
|
||||
public function another()
|
||||
{
|
||||
return $this->someMethod();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
use Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source\EventManager;
|
||||
|
||||
final class WithPropertyDependency
|
||||
{
|
||||
/**
|
||||
* @var EventManager
|
||||
*/
|
||||
private $eventManager;
|
||||
|
||||
public function __construct(EventManager $eventManager)
|
||||
{
|
||||
$this->eventManager = $eventManager;
|
||||
}
|
||||
|
||||
public function usingProperty()
|
||||
{
|
||||
return $this->eventManager->runEvent();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
use Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source\EventManager;
|
||||
|
||||
final class WithPropertyDependency
|
||||
{
|
||||
/**
|
||||
* @var EventManager
|
||||
*/
|
||||
private $eventManager;
|
||||
|
||||
public function __construct(EventManager $eventManager)
|
||||
{
|
||||
$this->eventManager = $eventManager;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source;
|
||||
|
||||
abstract class AbstractFather
|
||||
{
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source;
|
||||
|
||||
final class EventManager
|
||||
{
|
||||
public function runEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
final class ExtraClassName
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const VALUE = 'value';
|
||||
public function newMethodName()
|
||||
{
|
||||
return self::VALUE;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Fixture;
|
||||
|
||||
use Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source\EventManager;
|
||||
final class ExtraUsingProperty extends \Rector\Decouple\Tests\Rector\ClassMethod\DecoupleClassMethodToOwnClassRector\Source\AbstractFather
|
||||
{
|
||||
/**
|
||||
* @var EventManager
|
||||
*/
|
||||
private $eventManager;
|
||||
public function __construct($eventManager)
|
||||
{
|
||||
$this->eventManager = $eventManager;
|
||||
}
|
||||
public function newUsingProperty()
|
||||
{
|
||||
return $this->eventManager->runEvent();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user