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;
|
2020-07-29 13:55:33 +00:00
|
|
|
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
|
|
|
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
|
|
|
use Rector\Core\RectorDefinition\RectorDefinition;
|
2020-07-13 21:55:06 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
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
|
|
|
|
*/
|
|
|
|
public const OLD_TO_NEW_METHODS_BY_CLASS = '$oldToNewMethodsByClass';
|
|
|
|
|
2017-10-06 11:57:21 +00:00
|
|
|
/**
|
|
|
|
* class => [
|
|
|
|
* oldMethod => newMethod
|
|
|
|
* ]
|
|
|
|
*
|
2020-07-29 13:55:33 +00:00
|
|
|
* @var array<string, array<string, string>>
|
2017-10-06 11:57:21 +00:00
|
|
|
*/
|
2018-10-22 17:17:17 +00:00
|
|
|
private $oldToNewMethodsByClass = [];
|
2017-10-06 11:57:21 +00:00
|
|
|
|
2018-04-08 11:51:26 +00:00
|
|
|
public function getDefinition(): RectorDefinition
|
|
|
|
{
|
2018-08-01 13:34:55 +00:00
|
|
|
return new RectorDefinition('Turns method names to new ones.', [
|
2018-08-01 19:52:44 +00:00
|
|
|
new ConfiguredCodeSample(
|
2019-09-18 06:14:35 +00:00
|
|
|
<<<'PHP'
|
2018-10-22 17:39:10 +00:00
|
|
|
$someObject = new SomeExampleClass;
|
2018-04-09 12:48:15 +00:00
|
|
|
$someObject->oldMethod();
|
2019-09-18 06:14:35 +00:00
|
|
|
PHP
|
2018-04-08 11:51:26 +00:00
|
|
|
,
|
2019-09-18 06:14:35 +00:00
|
|
|
<<<'PHP'
|
2018-10-22 17:39:10 +00:00
|
|
|
$someObject = new SomeExampleClass;
|
2018-04-09 12:48:15 +00:00
|
|
|
$someObject->newMethod();
|
2019-09-18 06:14:35 +00:00
|
|
|
PHP
|
2018-08-01 19:52:44 +00:00
|
|
|
,
|
|
|
|
[
|
2020-07-30 15:46:36 +00:00
|
|
|
self::OLD_TO_NEW_METHODS_BY_CLASS => [
|
|
|
|
'SomeExampleClass' => [
|
|
|
|
'$oldToNewMethodsByClass' => [
|
|
|
|
'oldMethod' => 'newMethod',
|
|
|
|
],
|
2019-12-26 10:21:09 +00:00
|
|
|
],
|
2018-08-01 19:52:44 +00:00
|
|
|
],
|
|
|
|
]
|
|
|
|
),
|
2018-04-08 11:51:26 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2018-08-14 22:12:41 +00:00
|
|
|
/**
|
|
|
|
* @return string[]
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
{
|
2018-10-22 17:17:17 +00:00
|
|
|
foreach ($this->oldToNewMethodsByClass as $type => $oldToNewMethods) {
|
2019-09-04 12:10:29 +00:00
|
|
|
if (! $this->isMethodStaticCallOrClassMethodObjectType($node, $type)) {
|
2018-10-22 17:17:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-08-14 22:12:41 +00:00
|
|
|
|
2018-10-22 17:17:17 +00:00
|
|
|
foreach ($oldToNewMethods as $oldMethod => $newMethod) {
|
2019-10-24 09:25:30 +00:00
|
|
|
if (! $this->isName($node->name, $oldMethod)) {
|
2018-10-22 17:17:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-08-14 22:12:41 +00:00
|
|
|
|
2020-07-13 22:22:31 +00:00
|
|
|
if ($this->skipClassMethod($node, $newMethod, $type)) {
|
2020-07-13 21:55:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$newNode = $this->renameToMethod($node, $newMethod);
|
2019-01-14 18:21:23 +00:00
|
|
|
if ($newNode !== null) {
|
|
|
|
return $newNode;
|
2018-12-11 17:22:54 +00:00
|
|
|
}
|
2018-10-22 17:17:17 +00:00
|
|
|
}
|
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
|
|
|
|
{
|
|
|
|
$this->oldToNewMethodsByClass = $configuration[self::OLD_TO_NEW_METHODS_BY_CLASS] ?? [];
|
|
|
|
}
|
|
|
|
|
2018-12-10 14:45:35 +00:00
|
|
|
/**
|
2018-12-11 17:22:54 +00:00
|
|
|
* @param MethodCall|StaticCall|ClassMethod $node
|
|
|
|
* @param string|mixed[] $newMethod
|
2018-12-10 14:45:35 +00:00
|
|
|
*/
|
2020-07-13 21:55:06 +00:00
|
|
|
private function renameToMethod(Node $node, $newMethod): ?Node
|
2018-12-10 14:45:35 +00:00
|
|
|
{
|
2018-12-11 17:22:54 +00:00
|
|
|
if (is_string($newMethod)) {
|
|
|
|
$node->name = new Identifier($newMethod);
|
|
|
|
|
|
|
|
return $node;
|
|
|
|
}
|
|
|
|
|
|
|
|
// special case for array dim fetch
|
|
|
|
if (! $node instanceof ClassMethod) {
|
|
|
|
$node->name = new Identifier($newMethod['name']);
|
|
|
|
|
|
|
|
return new ArrayDimFetch($node, BuilderHelpers::normalizeValue($newMethod['array_key']));
|
2018-12-10 14:45:35 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 17:22:54 +00:00
|
|
|
return null;
|
2018-12-10 14:45:35 +00:00
|
|
|
}
|
2020-07-13 21:55:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string|mixed[] $newMethod
|
|
|
|
*/
|
2020-07-13 22:22:31 +00:00
|
|
|
private function shouldSkipForAlreadyExistingClassMethod(ClassMethod $classMethod, $newMethod): bool
|
2020-07-13 21:55:06 +00:00
|
|
|
{
|
|
|
|
if (! is_string($newMethod)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-13 22:22:31 +00:00
|
|
|
if (! $classMethod instanceof ClassMethod) {
|
2020-07-13 21:55:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @var ClassLike|null $classLike */
|
2020-07-13 22:22:31 +00:00
|
|
|
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
2020-07-13 21:55:06 +00:00
|
|
|
if ($classLike === null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (bool) $classLike->getMethod($newMethod);
|
|
|
|
}
|
2020-07-13 22:22:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param MethodCall|StaticCall|ClassMethod $node
|
2020-07-27 06:56:25 +00:00
|
|
|
* @param string|string[] $newMethod
|
2020-07-13 22:22:31 +00:00
|
|
|
*/
|
2020-07-27 06:56:25 +00:00
|
|
|
private function skipClassMethod(Node $node, $newMethod, string $type): bool
|
2020-07-13 22:22:31 +00:00
|
|
|
{
|
|
|
|
if (! $node instanceof ClassMethod) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->shouldSkipForAlreadyExistingClassMethod($node, $newMethod)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->shouldSkipForExactClassMethodForClassMethod($node, $type);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function shouldSkipForExactClassMethodForClassMethod(ClassMethod $classMethod, string $type): bool
|
|
|
|
{
|
|
|
|
return $classMethod->getAttribute(AttributeKey::CLASS_NAME) === $type;
|
|
|
|
}
|
2017-10-06 11:57:21 +00:00
|
|
|
}
|