2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
2017-10-06 11:57:21 +00:00
|
|
|
|
2019-09-23 15:09:26 +00:00
|
|
|
namespace Rector\Renaming\Rector\MethodCall;
|
2017-10-06 11:57:21 +00:00
|
|
|
|
2018-12-11 17:22:54 +00:00
|
|
|
use PhpParser\BuilderHelpers;
|
2017-10-06 11:57:21 +00:00
|
|
|
use PhpParser\Node;
|
2018-12-11 17:22:54 +00:00
|
|
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
2018-12-10 14:45:35 +00:00
|
|
|
use PhpParser\Node\Expr\MethodCall;
|
|
|
|
use PhpParser\Node\Expr\StaticCall;
|
2017-10-29 00:31:27 +00:00
|
|
|
use PhpParser\Node\Identifier;
|
2020-07-13 21:55:06 +00:00
|
|
|
use PhpParser\Node\Stmt\ClassLike;
|
2018-12-10 14:45:35 +00:00
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
2021-02-27 02:13:22 +00:00
|
|
|
use PHPStan\Type\ObjectType;
|
2020-07-29 13:55:33 +00:00
|
|
|
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
2021-02-15 16:21:29 +00:00
|
|
|
use Rector\Core\NodeManipulator\ClassManipulator;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
2020-07-13 21:55:06 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2020-08-24 22:26:14 +00:00
|
|
|
use Rector\Renaming\Contract\MethodCallRenameInterface;
|
|
|
|
use Rector\Renaming\ValueObject\MethodCallRename;
|
|
|
|
use Rector\Renaming\ValueObject\MethodCallRenameWithArrayKey;
|
2020-11-16 17:50:38 +00:00
|
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
|
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
2020-08-24 22:37:12 +00:00
|
|
|
use Webmozart\Assert\Assert;
|
2017-10-06 11:57:21 +00:00
|
|
|
|
2019-09-03 09:11:45 +00:00
|
|
|
/**
|
2019-09-23 15:03:23 +00:00
|
|
|
* @see \Rector\Renaming\Tests\Rector\MethodCall\RenameMethodRector\RenameMethodRectorTest
|
2019-09-03 09:11:45 +00:00
|
|
|
*/
|
2020-07-29 13:55:33 +00:00
|
|
|
final class RenameMethodRector extends AbstractRector implements ConfigurableRectorInterface
|
2017-10-06 11:57:21 +00:00
|
|
|
{
|
2020-07-29 13:55:33 +00:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
2020-08-25 00:21:00 +00:00
|
|
|
public const METHOD_CALL_RENAMES = 'method_call_renames';
|
2020-07-29 13:55:33 +00:00
|
|
|
|
2017-10-06 11:57:21 +00:00
|
|
|
/**
|
2020-08-24 22:26:14 +00:00
|
|
|
* @var MethodCallRenameInterface[]
|
2017-10-06 11:57:21 +00:00
|
|
|
*/
|
2020-08-24 22:26:14 +00:00
|
|
|
private $methodCallRenames = [];
|
2017-10-06 11:57:21 +00:00
|
|
|
|
2021-02-15 16:21:29 +00:00
|
|
|
/**
|
|
|
|
* @var ClassManipulator
|
|
|
|
*/
|
|
|
|
private $classManipulator;
|
|
|
|
|
|
|
|
public function __construct(ClassManipulator $classManipulator)
|
|
|
|
{
|
|
|
|
$this->classManipulator = $classManipulator;
|
|
|
|
}
|
|
|
|
|
2020-11-16 17:50:38 +00:00
|
|
|
public function getRuleDefinition(): RuleDefinition
|
2018-04-08 11:51:26 +00:00
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
return new RuleDefinition('Turns method names to new ones.', [
|
2018-08-01 19:52:44 +00:00
|
|
|
new ConfiguredCodeSample(
|
2020-09-15 08:23:13 +00:00
|
|
|
<<<'CODE_SAMPLE'
|
2018-10-22 17:39:10 +00:00
|
|
|
$someObject = new SomeExampleClass;
|
2018-04-09 12:48:15 +00:00
|
|
|
$someObject->oldMethod();
|
2020-09-15 08:23:13 +00:00
|
|
|
CODE_SAMPLE
|
2018-04-08 11:51:26 +00:00
|
|
|
,
|
2020-09-15 08:23:13 +00:00
|
|
|
<<<'CODE_SAMPLE'
|
2018-10-22 17:39:10 +00:00
|
|
|
$someObject = new SomeExampleClass;
|
2018-04-09 12:48:15 +00:00
|
|
|
$someObject->newMethod();
|
2020-09-15 08:23:13 +00:00
|
|
|
CODE_SAMPLE
|
2018-08-01 19:52:44 +00:00
|
|
|
,
|
|
|
|
[
|
2020-08-24 22:37:12 +00:00
|
|
|
self::METHOD_CALL_RENAMES => [
|
2020-08-24 22:26:14 +00:00
|
|
|
new MethodCallRename('SomeExampleClass', 'oldMethod', 'newMethod'),
|
2018-08-01 19:52:44 +00:00
|
|
|
],
|
|
|
|
]
|
|
|
|
),
|
2018-04-08 11:51:26 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2018-08-14 22:12:41 +00:00
|
|
|
/**
|
2021-02-27 00:06:15 +00:00
|
|
|
* @return array<class-string<Node>>
|
2018-08-14 22:12:41 +00:00
|
|
|
*/
|
|
|
|
public function getNodeTypes(): array
|
2017-10-06 11:57:21 +00:00
|
|
|
{
|
2018-12-11 17:22:54 +00:00
|
|
|
return [MethodCall::class, StaticCall::class, ClassMethod::class];
|
2017-10-06 11:57:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-12-11 17:22:54 +00:00
|
|
|
* @param MethodCall|StaticCall|ClassMethod $node
|
2017-10-06 11:57:21 +00:00
|
|
|
*/
|
|
|
|
public function refactor(Node $node): ?Node
|
|
|
|
{
|
2020-08-24 22:26:14 +00:00
|
|
|
foreach ($this->methodCallRenames as $methodCallRename) {
|
2021-02-15 16:21:29 +00:00
|
|
|
$implementsInterface = $this->classManipulator->hasParentMethodOrInterface(
|
2021-02-27 02:13:22 +00:00
|
|
|
$methodCallRename->getOldObjectType(),
|
2021-02-15 16:21:29 +00:00
|
|
|
$methodCallRename->getOldMethod()
|
|
|
|
);
|
|
|
|
if ($implementsInterface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-02-08 23:42:47 +00:00
|
|
|
if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
|
|
|
|
$node,
|
2021-02-27 02:13:22 +00:00
|
|
|
$methodCallRename->getOldObjectType()
|
2021-02-08 23:42:47 +00:00
|
|
|
)) {
|
2018-10-22 17:17:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-08-14 22:12:41 +00:00
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
if (! $this->isName($node->name, $methodCallRename->getOldMethod())) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-08-14 22:12:41 +00:00
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
if ($this->skipClassMethod($node, $methodCallRename)) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-13 21:55:06 +00:00
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
$node->name = new Identifier($methodCallRename->getNewMethod());
|
|
|
|
|
|
|
|
if ($methodCallRename instanceof MethodCallRenameWithArrayKey && ! $node instanceof ClassMethod) {
|
|
|
|
return new ArrayDimFetch($node, BuilderHelpers::normalizeValue($methodCallRename->getArrayKey()));
|
2018-10-22 17:17:17 +00:00
|
|
|
}
|
2020-08-24 22:26:14 +00:00
|
|
|
|
|
|
|
return $node;
|
2018-08-14 22:12:41 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 17:22:54 +00:00
|
|
|
return null;
|
2018-08-14 22:12:41 +00:00
|
|
|
}
|
2018-12-10 14:45:35 +00:00
|
|
|
|
2020-07-29 13:55:33 +00:00
|
|
|
public function configure(array $configuration): void
|
|
|
|
{
|
2020-08-24 22:37:12 +00:00
|
|
|
$methodCallRenames = $configuration[self::METHOD_CALL_RENAMES] ?? [];
|
|
|
|
Assert::allIsInstanceOf($methodCallRenames, MethodCallRenameInterface::class);
|
|
|
|
|
|
|
|
$this->methodCallRenames = $methodCallRenames;
|
2020-07-29 13:55:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 20:45:36 +00:00
|
|
|
/**
|
|
|
|
* @param MethodCall|StaticCall|ClassMethod $node
|
|
|
|
*/
|
2020-08-24 22:26:14 +00:00
|
|
|
private function skipClassMethod(Node $node, MethodCallRenameInterface $methodCallRename): bool
|
2020-08-05 20:45:36 +00:00
|
|
|
{
|
|
|
|
if (! $node instanceof ClassMethod) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
if ($this->shouldSkipForAlreadyExistingClassMethod($node, $methodCallRename)) {
|
2020-08-05 20:45:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-17 08:21:50 +00:00
|
|
|
return $this->shouldSkipForExactClassMethodForClassMethodOrTargetInvokePrivate(
|
|
|
|
$node,
|
2021-02-27 02:13:22 +00:00
|
|
|
$methodCallRename->getOldObjectType(),
|
2021-02-17 08:21:50 +00:00
|
|
|
$methodCallRename->getNewMethod()
|
|
|
|
);
|
2018-12-10 14:45:35 +00:00
|
|
|
}
|
2020-07-13 21:55:06 +00:00
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
private function shouldSkipForAlreadyExistingClassMethod(
|
|
|
|
ClassMethod $classMethod,
|
|
|
|
MethodCallRenameInterface $methodCallRename
|
|
|
|
): bool {
|
2020-07-13 22:22:31 +00:00
|
|
|
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
2021-01-19 11:24:48 +00:00
|
|
|
if (! $classLike instanceof ClassLike) {
|
2020-07-13 21:55:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-24 22:26:14 +00:00
|
|
|
return (bool) $classLike->getMethod($methodCallRename->getNewMethod());
|
2020-07-13 21:55:06 +00:00
|
|
|
}
|
2020-07-13 22:22:31 +00:00
|
|
|
|
2021-02-17 08:21:50 +00:00
|
|
|
private function shouldSkipForExactClassMethodForClassMethodOrTargetInvokePrivate(
|
|
|
|
ClassMethod $classMethod,
|
2021-02-27 02:13:22 +00:00
|
|
|
ObjectType $objectType,
|
2021-02-17 08:21:50 +00:00
|
|
|
string $newMethodName
|
|
|
|
): bool {
|
2021-02-10 15:21:14 +00:00
|
|
|
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
|
|
|
$methodCalls = $this->nodeRepository->findMethodCallsOnClass($className);
|
|
|
|
|
|
|
|
$name = $this->getName($classMethod->name);
|
|
|
|
if (isset($methodCalls[$name])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-02-27 02:13:22 +00:00
|
|
|
$classMethodClass = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
|
|
|
if ($classMethodClass === $objectType) {
|
2021-02-17 08:21:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($classMethod->isPublic()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$newClassMethod = clone $classMethod;
|
|
|
|
$newClassMethod->name = new Identifier($newMethodName);
|
2021-02-27 02:13:22 +00:00
|
|
|
|
2021-02-17 08:21:50 +00:00
|
|
|
return $newClassMethod->isMagic();
|
2020-07-13 22:22:31 +00:00
|
|
|
}
|
2017-10-06 11:57:21 +00:00
|
|
|
}
|