rector/src/Rector/Dynamic/MethodNameReplacerRector.php

221 lines
5.7 KiB
PHP
Raw Normal View History

2017-10-06 11:57:21 +00:00
<?php declare(strict_types=1);
namespace Rector\Rector\Dynamic;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
2017-10-06 11:57:21 +00:00
use PhpParser\Node\Name;
use Rector\Node\Attribute;
use Rector\NodeAnalyzer\MethodCallAnalyzer;
use Rector\NodeAnalyzer\MethodNameAnalyzer;
2017-10-20 16:23:59 +00:00
use Rector\NodeAnalyzer\StaticMethodCallAnalyzer;
use Rector\NodeChanger\IdentifierRenamer;
2017-10-06 11:57:21 +00:00
use Rector\Rector\AbstractRector;
final class MethodNameReplacerRector extends AbstractRector
{
/**
* class => [
* oldMethod => newMethod
* ]
*
* or (typically for static calls):
*
* class => [
* oldMethod => [
* newClass, newMethod
* ]
* ]
*
2017-11-01 11:46:45 +00:00
* @todo consider splitting to static call replacer or class rename,
* this api can lead users to bugs (already did)
*
2017-10-06 11:57:21 +00:00
* @var string[][]
*/
private $perClassOldToNewMethods = [];
/**
* @var string[]
*/
private $activeTypes = [];
/**
* @var MethodCallAnalyzer
*/
private $methodCallAnalyzer;
2017-10-20 16:25:47 +00:00
2017-10-20 16:23:59 +00:00
/**
* @var StaticMethodCallAnalyzer
*/
private $staticMethodCallAnalyzer;
/**
* @var MethodNameAnalyzer
*/
private $methodNameAnalyzer;
/**
* @var IdentifierRenamer
*/
private $identifierRenamer;
2017-12-26 18:31:39 +00:00
2017-10-06 11:57:21 +00:00
/**
2017-12-09 20:58:33 +00:00
* @param string[][] $perClassOldToNewMethods
2017-10-06 11:57:21 +00:00
*/
2017-10-20 16:23:59 +00:00
public function __construct(
array $perClassOldToNewMethods,
MethodCallAnalyzer $methodCallAnalyzer,
StaticMethodCallAnalyzer $staticMethodCallAnalyzer,
MethodNameAnalyzer $methodNameAnalyzer,
IdentifierRenamer $identifierRenamer
2017-10-20 16:23:59 +00:00
) {
2017-10-06 11:57:21 +00:00
$this->perClassOldToNewMethods = $perClassOldToNewMethods;
$this->methodCallAnalyzer = $methodCallAnalyzer;
2017-10-20 16:23:59 +00:00
$this->staticMethodCallAnalyzer = $staticMethodCallAnalyzer;
$this->methodNameAnalyzer = $methodNameAnalyzer;
$this->identifierRenamer = $identifierRenamer;
2017-10-06 11:57:21 +00:00
}
public function isCandidate(Node $node): bool
{
2017-11-06 12:58:51 +00:00
$this->activeTypes = [];
2017-10-06 11:57:21 +00:00
$matchedTypes = $this->methodCallAnalyzer->matchTypes($node, $this->getClasses());
if ($matchedTypes) {
$this->activeTypes = $matchedTypes;
2017-10-06 11:57:21 +00:00
return true;
}
$matchedTypes = $this->staticMethodCallAnalyzer->matchTypes($node, $this->getClasses());
if ($matchedTypes) {
$this->activeTypes = $matchedTypes;
2017-10-06 11:57:21 +00:00
return true;
}
return $this->isMethodName($node, $this->getClasses());
2017-10-06 11:57:21 +00:00
}
/**
* @param Identifier|StaticCall|MethodCall $node
2017-10-06 11:57:21 +00:00
*/
public function refactor(Node $node): ?Node
{
if ($node instanceof Identifier) {
return $this->resolveIdentifier($node);
}
$oldToNewMethods = $this->matchOldToNewMethods();
2017-12-10 00:13:25 +00:00
/** @var Identifier $identifierNode */
$identifierNode = $node->name;
$methodName = $identifierNode->toString();
2017-10-20 16:59:24 +00:00
if (! isset($oldToNewMethods[$methodName])) {
return $node;
}
2017-12-10 00:13:25 +00:00
if ($node instanceof StaticCall && $this->isClassRename($oldToNewMethods)) {
return $this->resolveClassRename($node, $oldToNewMethods, $methodName);
2017-10-20 16:59:24 +00:00
}
$this->identifierRenamer->renameNode($node, $oldToNewMethods[$methodName]);
2017-12-25 19:37:09 +00:00
return $node;
2017-10-06 11:57:21 +00:00
}
/**
* @return string[]
*/
private function getClasses(): array
{
return array_keys($this->perClassOldToNewMethods);
}
/**
* @param mixed[] $oldToNewMethods
*/
private function isClassRename(array $oldToNewMethods): bool
{
$firstMethodConfiguration = current($oldToNewMethods);
return is_array($firstMethodConfiguration);
}
/**
* @return string[]
*/
2017-11-14 20:43:57 +00:00
private function matchOldToNewMethods(): array
{
foreach ($this->activeTypes as $activeType) {
if ($this->perClassOldToNewMethods[$activeType]) {
return $this->perClassOldToNewMethods[$activeType];
}
}
return [];
}
/**
* @param string[] $types
*/
private function isMethodName(Node $node, array $types): bool
{
// already covered by previous methods
if (! $node instanceof Identifier) {
return false;
}
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
if ($parentNode instanceof MethodCall || $parentNode instanceof StaticCall) {
return false;
}
if (! $this->methodNameAnalyzer->isOverrideOfTypes($node, $types)) {
return false;
}
2017-11-06 11:48:43 +00:00
/** @var Identifier $node */
$parentClassName = $node->getAttribute(Attribute::PARENT_CLASS_NAME);
/** @var Identifier $node */
if (! isset($this->perClassOldToNewMethods[$parentClassName][$node->name])) {
return false;
}
$this->activeTypes = [$parentClassName];
return true;
}
private function resolveIdentifier(Identifier $node): Node
{
2017-11-14 20:43:57 +00:00
$oldToNewMethods = $this->matchOldToNewMethods();
$methodName = $node->name;
if (! isset($oldToNewMethods[$methodName])) {
return $node;
}
$node->name = $oldToNewMethods[$methodName];
return $node;
}
/**
* @param string[] $oldToNewMethods
*/
2017-12-10 00:13:25 +00:00
private function resolveClassRename(StaticCall $staticCallNode, array $oldToNewMethods, string $methodName): Node
{
[$newClass, $newMethod] = $oldToNewMethods[$methodName];
2017-12-10 00:13:25 +00:00
$staticCallNode->class = new Name($newClass);
$this->identifierRenamer->renameNode($staticCallNode, $newMethod);
2017-12-10 00:13:25 +00:00
return $staticCallNode;
}
2017-10-06 11:57:21 +00:00
}