mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-24 19:53:14 +01:00
Merge pull request #77 from RectorPHP/deprecation-extract
[DeprecationExtractor] improvements, guess only - more reliable
This commit is contained in:
commit
5916eb2a36
@ -14,7 +14,7 @@
|
||||
"roave/better-reflection": "^2.0",
|
||||
"symfony/console": "^3.3",
|
||||
"symfony/dependency-injection": "^3.3",
|
||||
"symplify/package-builder": "^2.4"
|
||||
"symplify/package-builder": "^2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^0.8",
|
||||
@ -23,7 +23,7 @@
|
||||
"symfony/expression-language": "^3.3",
|
||||
"symfony/dependency-injection": "^3.3",
|
||||
"symfony/form": "^3.3",
|
||||
"symplify/easy-coding-standard": "^2.4",
|
||||
"symplify/easy-coding-standard": "^2.5",
|
||||
"tracy/tracy": "^2.4"
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -54,11 +54,7 @@ parameters:
|
||||
Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer:
|
||||
# classes might not exist
|
||||
- */src/Rector/Contrib/*/*Rector.php
|
||||
- packages/DeprecationExtractor/tests/Rector/RectorFactoryTest.php
|
||||
- packages/NodeTypeResolver/src/NodeVisitor/TypeResolver.php
|
||||
Symplify\CodingStandard\Sniffs\Classes\EqualInterfaceImplementationSniff:
|
||||
# empty parent interface, disable for now
|
||||
- packages/DeprecationExtractor/src/Deprecation/ClassMethodDeprecation.php
|
||||
Symplify\CodingStandard\Sniffs\Debug\CommentedOutCodeSniff:
|
||||
# false positive - @todo fix
|
||||
- packages/NodeTypeResolver/src/NodeVisitor/TypeResolver.php
|
||||
|
@ -10,10 +10,16 @@ It helps to generated automate rectors and identify BC changes purely from the c
|
||||
|
||||
## How it works?
|
||||
|
||||
1. Just read the message
|
||||
2. Detect what code should be change into what code
|
||||
3. Create `Deprecation` class that holds information about the change - e.g. `ClassMethodRepcation`
|
||||
4. Create dynamic rectors that will do the work
|
||||
1. Just read the message or node
|
||||
2. Guess what rector might change the code
|
||||
3. Create `RectorGuess` class that holds rector suggetion
|
||||
|
||||
- suggested rector class
|
||||
- certainity of the guess (from 0 to 100)
|
||||
- related node
|
||||
- related message (only for `@deprecate`)
|
||||
|
||||
4. Show it in console table
|
||||
|
||||
|
||||
## How it helps you?
|
||||
@ -25,5 +31,3 @@ vendor/bin/rector extract-deprecations vendor/nette/application/src
|
||||
```
|
||||
|
||||
It will show you what changed and how.
|
||||
|
||||
Moreover, it will change the code for you instead of writing manuall rectors.
|
||||
|
@ -4,14 +4,20 @@ namespace Rector\DeprecationExtractor\Console\Command;
|
||||
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\DeprecationExtractor;
|
||||
use Rector\DeprecationExtractor\Rector\RectorGuesser;
|
||||
use Rector\Naming\CommandNaming;
|
||||
use Rector\Node\Attribute;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
// @todo match the name...
|
||||
/**
|
||||
* This commands shows suggested rector to use to resolve deprecation.
|
||||
*
|
||||
* We need this manually to analyze other versions and do not actually create any rector.
|
||||
*/
|
||||
final class ExtractDeprecationsCommand extends Command
|
||||
{
|
||||
/**
|
||||
@ -34,13 +40,20 @@ final class ExtractDeprecationsCommand extends Command
|
||||
*/
|
||||
private $symfonyStyle;
|
||||
|
||||
/**
|
||||
* @var RectorGuesser
|
||||
*/
|
||||
private $rectorGuesser;
|
||||
|
||||
public function __construct(
|
||||
DeprecationExtractor $deprecationExtractor,
|
||||
DeprecationCollector $deprecationCollector,
|
||||
RectorGuesser $rectorGuesser,
|
||||
SymfonyStyle $symfonyStyle
|
||||
) {
|
||||
$this->deprecationExtractor = $deprecationExtractor;
|
||||
$this->deprecationCollector = $deprecationCollector;
|
||||
$this->rectorGuesser = $rectorGuesser;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
|
||||
parent::__construct();
|
||||
@ -59,16 +72,35 @@ final class ExtractDeprecationsCommand extends Command
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$source = $input->getArgument(self::ARGUMENT_SOURCE_NAME);
|
||||
$this->deprecationExtractor->scanDirectories($source);
|
||||
$this->deprecationExtractor->scanDirectories(
|
||||
$input->getArgument(self::ARGUMENT_SOURCE_NAME)
|
||||
);
|
||||
|
||||
$this->symfonyStyle->note(sprintf(
|
||||
'Found %d deprecations.',
|
||||
$this->symfonyStyle->title(sprintf(
|
||||
'Found %d deprecations',
|
||||
count($this->deprecationCollector->getDeprecations())
|
||||
));
|
||||
|
||||
foreach ($this->deprecationCollector->getDeprecations() as $deprecation) {
|
||||
$output->writeln($deprecation);
|
||||
$guessedRectors = $this->rectorGuesser->guessForDeprecations($this->deprecationCollector->getDeprecations());
|
||||
|
||||
foreach ($guessedRectors as $guessedRector) {
|
||||
if ($guessedRector->isUseful() === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->symfonyStyle->success($guessedRector->getGuessedRectorClass());
|
||||
|
||||
$this->symfonyStyle->writeln(' ' . $guessedRector->getMessage());
|
||||
$this->symfonyStyle->newLine();
|
||||
|
||||
$node = $guessedRector->getNode();
|
||||
|
||||
$this->symfonyStyle->writeln(' Namespace: ' . $node->getAttribute(Attribute::NAMESPACE));
|
||||
$this->symfonyStyle->writeln(' Class: ' . $node->getAttribute(Attribute::CLASS_NAME));
|
||||
$this->symfonyStyle->writeln(' Scope: ' . $node->getAttribute(Attribute::SCOPE));
|
||||
$this->symfonyStyle->writeln(' Related node: ' . get_class($node));
|
||||
|
||||
$this->symfonyStyle->newLine(2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,7 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Contract\Deprecation;
|
||||
|
||||
interface DeprecationInterface
|
||||
{
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
|
||||
final class ClassDeprecation implements DeprecationInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $oldClass;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $newClass;
|
||||
|
||||
public function __construct(string $oldClass, string $newClass)
|
||||
{
|
||||
$this->oldClass = $oldClass;
|
||||
$this->newClass = $newClass;
|
||||
}
|
||||
|
||||
public function getOldClass(): string
|
||||
{
|
||||
return $this->oldClass;
|
||||
}
|
||||
|
||||
public function getNewClass(): string
|
||||
{
|
||||
return $this->newClass;
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
|
||||
final class ClassMethodDeprecation implements DeprecationInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $oldMethod;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $newMethod;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $oldClass;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $newClass;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $oldArguments = [];
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $newArguments = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $oldArguments
|
||||
* @param mixed[] $newArguments
|
||||
*/
|
||||
public function __construct(
|
||||
string $oldMethod,
|
||||
string $newMethod,
|
||||
array $oldArguments = [],
|
||||
array $newArguments = []
|
||||
) {
|
||||
if (Strings::contains($oldMethod, '::')) {
|
||||
[$this->oldClass, $this->oldMethod] = explode('::', $oldMethod);
|
||||
} else {
|
||||
$this->oldMethod = $oldMethod;
|
||||
}
|
||||
|
||||
$this->oldMethod = rtrim($this->oldMethod, '()');
|
||||
|
||||
if (Strings::contains($newMethod, '::')) {
|
||||
[$this->newClass, $this->newMethod] = explode('::', $newMethod);
|
||||
} else {
|
||||
$this->newMethod = $newMethod;
|
||||
}
|
||||
|
||||
$this->newMethod = rtrim($this->newMethod, '()');
|
||||
|
||||
$this->oldArguments = $oldArguments;
|
||||
$this->newArguments = $newArguments;
|
||||
}
|
||||
|
||||
public function getOldClass(): string
|
||||
{
|
||||
return $this->oldClass;
|
||||
}
|
||||
|
||||
public function getNewClass(): string
|
||||
{
|
||||
return $this->newClass;
|
||||
}
|
||||
|
||||
public function getOldMethod(): string
|
||||
{
|
||||
return $this->oldMethod;
|
||||
}
|
||||
|
||||
public function getNewMethod(): string
|
||||
{
|
||||
return $this->newMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getOldArguments(): array
|
||||
{
|
||||
return $this->oldArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getNewArguments(): array
|
||||
{
|
||||
return $this->newArguments;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
final class Deprecation
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $message;
|
||||
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $node;
|
||||
|
||||
private function __construct(string $message, Node $node)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
public static function createFromMessageAndNode(string $message, Node $node): self
|
||||
{
|
||||
return new self($message, $node);
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getNode(): Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
}
|
@ -3,125 +3,28 @@
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Transformer\ArgumentToDeprecationTransformer;
|
||||
use Rector\DeprecationExtractor\Transformer\MessageToDeprecationTransformer;
|
||||
|
||||
/**
|
||||
* Collected from "deprecated" annotations and
|
||||
* from trigger_error(*, E_USER_DEPRECATED) function calls
|
||||
*/
|
||||
final class DeprecationCollector
|
||||
{
|
||||
/**
|
||||
* Collected from "deprecated" annotations
|
||||
*
|
||||
* @var string[]|Node[]
|
||||
*/
|
||||
private $deprecationMessages = [];
|
||||
|
||||
/**
|
||||
* Collected from trigger_error(*, E_USER_DEPRECATED) function calls
|
||||
*
|
||||
* @var Arg[]
|
||||
*/
|
||||
private $deprecationArgNodes = [];
|
||||
|
||||
/**
|
||||
* @var DeprecationInterface[]
|
||||
* @var Deprecation[]
|
||||
*/
|
||||
private $deprecations = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $areDeprecationsTransformed = false;
|
||||
|
||||
/**
|
||||
* @var MessageToDeprecationTransformer
|
||||
*/
|
||||
private $messageToDeprecationTransformer;
|
||||
|
||||
/**
|
||||
* @var ArgumentToDeprecationTransformer
|
||||
*/
|
||||
private $argumentToDeprecationTransformer;
|
||||
|
||||
public function __construct(
|
||||
MessageToDeprecationTransformer $messageToDeprecationTransformer,
|
||||
ArgumentToDeprecationTransformer $argumentToDeprecationTransformer
|
||||
) {
|
||||
$this->messageToDeprecationTransformer = $messageToDeprecationTransformer;
|
||||
$this->argumentToDeprecationTransformer = $argumentToDeprecationTransformer;
|
||||
}
|
||||
|
||||
public function addDeprecationMessage(string $deprecationMessage, Node $node): void
|
||||
public function addDeprecation(string $message, Node $node): void
|
||||
{
|
||||
$this->deprecationMessages[] = [
|
||||
'message' => $deprecationMessage,
|
||||
'node' => $node,
|
||||
];
|
||||
}
|
||||
|
||||
public function addDeprecationArgNode(Arg $argNode): void
|
||||
{
|
||||
$this->deprecationArgNodes[] = $argNode;
|
||||
$this->deprecations[] = Deprecation::createFromMessageAndNode($message, $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]|Node[]
|
||||
*/
|
||||
public function getDeprecationMessages(): array
|
||||
{
|
||||
return $this->deprecationMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Arg[]
|
||||
*/
|
||||
public function getDeprecationArgNodes(): array
|
||||
{
|
||||
return $this->deprecationArgNodes;
|
||||
}
|
||||
|
||||
public function addDeprecation(DeprecationInterface $deprecation): void
|
||||
{
|
||||
$this->deprecations[] = $deprecation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DeprecationInterface[]
|
||||
* @return Deprecation[]
|
||||
*/
|
||||
public function getDeprecations(): array
|
||||
{
|
||||
if (! $this->areDeprecationsTransformed) {
|
||||
$this->transformDeprecations();
|
||||
}
|
||||
|
||||
$this->areDeprecationsTransformed = true;
|
||||
|
||||
return $this->deprecations;
|
||||
}
|
||||
|
||||
/**
|
||||
* perform on getDeprecationArgNodes/addDeprecationMessage?
|
||||
*/
|
||||
private function transformDeprecations(): void
|
||||
{
|
||||
foreach ($this->deprecationMessages as $deprecationMessage) {
|
||||
$deprecation = $this->messageToDeprecationTransformer->transform(
|
||||
$deprecationMessage['message'],
|
||||
$deprecationMessage['node']
|
||||
);
|
||||
|
||||
if ($deprecation) {
|
||||
$this->addDeprecation($deprecation);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->deprecationArgNodes as $deprecationArgNode) {
|
||||
$deprecation = $this->argumentToDeprecationTransformer->transform($deprecationArgNode);
|
||||
|
||||
if ($deprecation) {
|
||||
$this->addDeprecation($deprecation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
|
||||
final class RemovedClassMethodDeprecation implements DeprecationInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method;
|
||||
|
||||
public function __construct(string $class, string $method)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Deprecation;
|
||||
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
|
||||
final class RemovedFunctionalityDeprecation implements DeprecationInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $message;
|
||||
|
||||
public function __construct(string $message)
|
||||
{
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
@ -5,9 +5,9 @@ namespace Rector\DeprecationExtractor;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Contract\Parser\ParserInterface;
|
||||
use Rector\DeprecationExtractor\NodeVisitor\DeprecationDetector;
|
||||
use Rector\FileSystem\PhpFilesFinder;
|
||||
use Rector\NodeTraverser\NodeTraverserFactory;
|
||||
use Rector\NodeTraverser\StandaloneTraverseNodeTraverser;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
final class DeprecationExtractor
|
||||
{
|
||||
@ -21,21 +21,27 @@ final class DeprecationExtractor
|
||||
*/
|
||||
private $standaloneTraverseNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var PhpFilesFinder
|
||||
*/
|
||||
private $phpFilesFinder;
|
||||
|
||||
/**
|
||||
* @var NodeTraverser
|
||||
*/
|
||||
private $mainNodeTraverser;
|
||||
private $deprecationDetectorNodeTraverser;
|
||||
|
||||
public function __construct(
|
||||
ParserInterface $parser,
|
||||
DeprecationDetector $deprecationDetector,
|
||||
StandaloneTraverseNodeTraverser $standaloneTraverseNodeTraverser
|
||||
StandaloneTraverseNodeTraverser $standaloneTraverseNodeTraverser,
|
||||
PhpFilesFinder $phpFilesFinder,
|
||||
NodeTraverserFactory $nodeTraverserFactory
|
||||
) {
|
||||
$this->parser = $parser;
|
||||
$this->standaloneTraverseNodeTraverser = $standaloneTraverseNodeTraverser;
|
||||
|
||||
$this->mainNodeTraverser = new NodeTraverser;
|
||||
$this->mainNodeTraverser->addVisitor($deprecationDetector);
|
||||
$this->deprecationDetectorNodeTraverser = $nodeTraverserFactory->createWithNodeVisitor($deprecationDetector);
|
||||
$this->phpFilesFinder = $phpFilesFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,28 +49,14 @@ final class DeprecationExtractor
|
||||
*/
|
||||
public function scanDirectories(array $directories): void
|
||||
{
|
||||
$files = $this->findPhpFilesInDirectories($directories);
|
||||
$files = $this->phpFilesFinder->findInDirectories($directories);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$nodes = $this->parser->parseFile($file->getRealPath());
|
||||
// this completes parent & child nodes, types and classses
|
||||
$this->standaloneTraverseNodeTraverser->traverse($nodes);
|
||||
$this->mainNodeTraverser->traverse($nodes);
|
||||
|
||||
$this->deprecationDetectorNodeTraverser->traverse($nodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $directories
|
||||
* @return SplFileInfo[] array
|
||||
*/
|
||||
private function findPhpFilesInDirectories(array $directories): array
|
||||
{
|
||||
$finder = Finder::create()
|
||||
->files()
|
||||
->name('*.php')
|
||||
->exclude(['tests', 'Tests']) // deprecations won't be in tests
|
||||
->in($directories);
|
||||
|
||||
return iterator_to_array($finder->getIterator());
|
||||
}
|
||||
}
|
||||
|
@ -46,10 +46,10 @@ final class TriggerErrorAnalyzer
|
||||
return $constFetchNode->name->toString() === 'E_USER_DEPRECATED';
|
||||
}
|
||||
|
||||
public function messageNodeForNode(FuncCall $triggerErrorFuncCallNode): Arg
|
||||
{
|
||||
return $this->messageNodePerTriggerErrorNode[$triggerErrorFuncCallNode];
|
||||
}
|
||||
// public function messageNodeForNode(FuncCall $triggerErrorFuncCallNode): Arg
|
||||
// {
|
||||
// return $this->messageNodePerTriggerErrorNode[$triggerErrorFuncCallNode];
|
||||
// }
|
||||
|
||||
private function isFunctionWithName(Node $node, string $name): bool
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\NodeAnalyzer\TriggerErrorAnalyzer;
|
||||
use Rector\NodeAnalyzer\DocBlockAnalyzer;
|
||||
use Rector\NodeValueResolver\NodeValueResolver;
|
||||
|
||||
/**
|
||||
* Inspired by https://github.com/sensiolabs-de/deprecation-detector/blob/master/src/Visitor/Deprecation/FindDeprecatedTagsVisitor.php
|
||||
@ -29,14 +30,21 @@ final class DeprecationDetector extends NodeVisitorAbstract
|
||||
*/
|
||||
private $triggerErrorAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NodeValueResolver
|
||||
*/
|
||||
private $nodeValueResolver;
|
||||
|
||||
public function __construct(
|
||||
DeprecationCollector $deprecationCollector,
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
TriggerErrorAnalyzer $triggerErrorAnalyzer
|
||||
TriggerErrorAnalyzer $triggerErrorAnalyzer,
|
||||
NodeValueResolver $nodeValueResolver
|
||||
) {
|
||||
$this->deprecationCollector = $deprecationCollector;
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->triggerErrorAnalyzer = $triggerErrorAnalyzer;
|
||||
$this->nodeValueResolver = $nodeValueResolver;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): void
|
||||
@ -49,8 +57,14 @@ final class DeprecationDetector extends NodeVisitorAbstract
|
||||
|
||||
if ($this->triggerErrorAnalyzer->isUserDeprecation($node)) {
|
||||
/** @var FuncCall $node */
|
||||
$argNode = $this->triggerErrorAnalyzer->messageNodeForNode($node);
|
||||
$this->deprecationCollector->addDeprecationArgNode($argNode);
|
||||
$argNode = $node->args[0];
|
||||
|
||||
$message = $this->nodeValueResolver->resolve($argNode);
|
||||
if ($message === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->deprecationCollector->addDeprecation($message, $node);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -63,6 +77,6 @@ final class DeprecationDetector extends NodeVisitorAbstract
|
||||
return;
|
||||
}
|
||||
|
||||
$this->deprecationCollector->addDeprecationMessage($deprecation, $node);
|
||||
$this->deprecationCollector->addDeprecation($deprecation, $node);
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Rector;
|
||||
|
||||
use Rector\Contract\Rector\RectorInterface;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassMethodDeprecation;
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\Deprecation\RemovedFunctionalityDeprecation;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Rector\Dynamic\MethodNameReplacerRector;
|
||||
|
||||
/**
|
||||
* Creates rectors with propper setup based on found deprecations.
|
||||
*/
|
||||
final class RectorFactory
|
||||
{
|
||||
/**
|
||||
* @var DeprecationCollector
|
||||
*/
|
||||
private $deprecationCollector;
|
||||
|
||||
public function __construct(DeprecationCollector $deprecationCollector)
|
||||
{
|
||||
$this->deprecationCollector = $deprecationCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RectorInterface[]
|
||||
*/
|
||||
public function createRectors(): array
|
||||
{
|
||||
$rectors = [];
|
||||
|
||||
foreach ($this->deprecationCollector->getDeprecations() as $deprecation) {
|
||||
if ($deprecation instanceof RemovedFunctionalityDeprecation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rectors[] = $this->createRectorFromDeprecation($deprecation);
|
||||
}
|
||||
|
||||
return $rectors;
|
||||
}
|
||||
|
||||
public function createRectorFromDeprecation(DeprecationInterface $deprecation): RectorInterface
|
||||
{
|
||||
if ($deprecation instanceof ClassMethodDeprecation) {
|
||||
return new MethodNameReplacerRector([
|
||||
$deprecation->getOldClass() => [
|
||||
$deprecation->getOldMethod() => $deprecation->getNewMethod(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'%s() was unable to create a Rector based on "%s" Deprecation. Create a new method there.',
|
||||
__METHOD__,
|
||||
get_class($deprecation)
|
||||
));
|
||||
}
|
||||
}
|
81
packages/DeprecationExtractor/src/Rector/RectorGuesser.php
Normal file
81
packages/DeprecationExtractor/src/Rector/RectorGuesser.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Rector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\DeprecationExtractor\Deprecation\Deprecation;
|
||||
use Rector\DeprecationExtractor\RectorGuess\RectorGuess;
|
||||
use Rector\DeprecationExtractor\RectorGuess\RectorGuessFactory;
|
||||
|
||||
/**
|
||||
* This class tries to guess, which Rector could be used to create refactoring
|
||||
* based on deprecation message and related options.
|
||||
*/
|
||||
final class RectorGuesser
|
||||
{
|
||||
/**
|
||||
* @var RectorGuessFactory
|
||||
*/
|
||||
private $rectorGuessFactory;
|
||||
|
||||
/**
|
||||
* @var UnsupportedDeprecationFilter
|
||||
*/
|
||||
private $unsupportedDeprecationFilter;
|
||||
|
||||
public function __construct(
|
||||
RectorGuessFactory $rectorGuessFactory,
|
||||
UnsupportedDeprecationFilter $unsupportedDeprecationFilter
|
||||
) {
|
||||
$this->rectorGuessFactory = $rectorGuessFactory;
|
||||
$this->unsupportedDeprecationFilter = $unsupportedDeprecationFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Deprecation[] $deprecations
|
||||
* @return RectorGuess[]
|
||||
*/
|
||||
public function guessForDeprecations(array $deprecations): array
|
||||
{
|
||||
$guessedRectors = [];
|
||||
|
||||
foreach ($deprecations as $deprecation) {
|
||||
$guessedRectors[] = $this->guessForDeprecation($deprecation);
|
||||
}
|
||||
|
||||
return $guessedRectors;
|
||||
}
|
||||
|
||||
private function guessForDeprecation(Deprecation $deprecation): ?RectorGuess
|
||||
{
|
||||
$message = $deprecation->getMessage();
|
||||
|
||||
if ($this->unsupportedDeprecationFilter->matches($deprecation)) {
|
||||
return $this->rectorGuessFactory->createUnsupported($message, $deprecation->getNode());
|
||||
}
|
||||
|
||||
if (Strings::contains($message, 'It will be made mandatory in') ||
|
||||
Strings::contains($message, 'Not defining it is deprecated since')
|
||||
) {
|
||||
return $this->rectorGuessFactory->createNewArgument($message, $deprecation->getNode());
|
||||
}
|
||||
|
||||
$result = Strings::split($message, '#use |Use#');
|
||||
|
||||
if (count($result) === 2) {
|
||||
if (Strings::contains($message, 'class is deprecated')) {
|
||||
return $this->rectorGuessFactory->createClassReplacer(
|
||||
$message,
|
||||
$deprecation->getNode()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->rectorGuessFactory->createMethodNameReplacerGuess(
|
||||
$message,
|
||||
$deprecation->getNode()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->rectorGuessFactory->createRemoval($message, $deprecation->getNode());
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Rector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\DeprecationExtractor\Deprecation\Deprecation;
|
||||
|
||||
final class UnsupportedDeprecationFilter
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $yamlDeprecationMessages = [
|
||||
'Autowiring-types are deprecated',
|
||||
'The "=" suffix that used to disable strict references',
|
||||
'The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported',
|
||||
'The "strict" attribute used when referencing the "" service is deprecated',
|
||||
'Service names that start with an underscore are deprecated',
|
||||
'configuration key',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $serviceDeprecationMessages = [
|
||||
'It should either be deprecated or its implementation upgraded.',
|
||||
'It should either be deprecated or its factory upgraded.',
|
||||
'Service identifiers will be made case sensitive',
|
||||
'Generating a dumped container without populating the method map is deprecated',
|
||||
'Dumping an uncompiled ContainerBuilder is deprecated',
|
||||
'service is private, ',
|
||||
'service is already initialized, ',
|
||||
'Relying on its factory\'s return-type to define the class of service',
|
||||
];
|
||||
|
||||
public function matches(Deprecation $deprecation): bool
|
||||
{
|
||||
foreach ($this->yamlDeprecationMessages as $yamlDeprecationMessage) {
|
||||
if (Strings::contains($deprecation->getMessage(), $yamlDeprecationMessage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->serviceDeprecationMessages as $serviceDeprecationMessage) {
|
||||
if (Strings::contains($deprecation->getMessage(), $serviceDeprecationMessage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\RectorGuess;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
final class RectorGuess
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const TYPE_REMOVAL = 'REMOVAL';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const TYPE_UNSUPPORTED = 'UNSUPPORTED';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $guessedRectorClass;
|
||||
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $message;
|
||||
|
||||
public function __construct(
|
||||
string $rectorClassOrType,
|
||||
Node $node,
|
||||
string $message
|
||||
) {
|
||||
$this->guessedRectorClass = $rectorClassOrType;
|
||||
$this->node = $node;
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getGuessedRectorClass(): string
|
||||
{
|
||||
return $this->guessedRectorClass;
|
||||
}
|
||||
|
||||
public function getNode(): Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function isUseful(): bool
|
||||
{
|
||||
return class_exists($this->guessedRectorClass);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\RectorGuess;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\Rector\Dynamic\ClassReplacerRector;
|
||||
use Rector\Rector\Dynamic\MethodArgumentChangerRector;
|
||||
use Rector\Rector\Dynamic\MethodNameReplacerRector;
|
||||
|
||||
final class RectorGuessFactory
|
||||
{
|
||||
public function createClassReplacer(string $message, Node $node): RectorGuess
|
||||
{
|
||||
return new RectorGuess(ClassReplacerRector::class, $node, $message);
|
||||
}
|
||||
|
||||
public function createMethodNameReplacerGuess(string $message, Node $node): RectorGuess
|
||||
{
|
||||
return new RectorGuess(MethodNameReplacerRector::class, $node, $message);
|
||||
}
|
||||
|
||||
public function createNewArgument(string $message, Node $node): RectorGuess
|
||||
{
|
||||
return new RectorGuess(MethodArgumentChangerRector::class, $node, $message);
|
||||
}
|
||||
|
||||
public function createRemoval(string $message, Node $node): RectorGuess
|
||||
{
|
||||
return new RectorGuess(RectorGuess::TYPE_REMOVAL, $node, $message);
|
||||
}
|
||||
|
||||
public function createUnsupported(string $message, Node $node): RectorGuess
|
||||
{
|
||||
return new RectorGuess(RectorGuess::TYPE_UNSUPPORTED, $node, $message);
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\RegExp;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
final class ClassAndMethodMatcher
|
||||
{
|
||||
/**
|
||||
* Matches:
|
||||
* - SomeClass
|
||||
* - SomeNamespace\AnotherClass
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const CLASS_PATTERN = '[A-Za-z\\\\]+';
|
||||
|
||||
/**
|
||||
* Matches:
|
||||
* - isMethod()
|
||||
* - isMethod('arg')
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const METHOD_PATTERN = '[A-Za-z]+\([A-Za-z\']*\)';
|
||||
|
||||
/**
|
||||
* Same as @see self::METHOD_PATTERN
|
||||
*
|
||||
* Just ignores (arguments), so:
|
||||
* - isMethod('arg')
|
||||
* will match and return:
|
||||
* - isMethod()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const METHOD_WITHOUT_ARGUMENTS_PATTERN = '[A-Za-z]+';
|
||||
|
||||
/**
|
||||
* Matches:
|
||||
* - Use <class> instead
|
||||
* - Use the <class> instead
|
||||
* - Use the <class> class instead
|
||||
* - use the <class> class instead
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const CLASS_METHOD_INSTEAD_PATTERN = '#use( the)? (?<classMethod>' .
|
||||
self::CLASS_PATTERN .
|
||||
')( class)? instead#i';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CLASS_METHOD_PATTERN = '#^(?<classMethod>' .
|
||||
self::CLASS_PATTERN .
|
||||
'::' .
|
||||
self::METHOD_PATTERN .
|
||||
')#s';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CLASS_METHOD_WITHOUT_ARGUMENTS_PATTERN = '#^(?<classMethod>' .
|
||||
self::CLASS_PATTERN .
|
||||
'::' .
|
||||
self::METHOD_WITHOUT_ARGUMENTS_PATTERN .
|
||||
')#s';
|
||||
|
||||
public function matchClassWithMethod(string $content): string
|
||||
{
|
||||
$result = Strings::match($content, self::CLASS_METHOD_PATTERN);
|
||||
|
||||
return $result['classMethod'] ?? '';
|
||||
}
|
||||
|
||||
public function matchClassWithMethodWithoutArguments(string $content): string
|
||||
{
|
||||
$result = Strings::match($content, self::CLASS_METHOD_WITHOUT_ARGUMENTS_PATTERN);
|
||||
|
||||
return $result['classMethod'] ?? '';
|
||||
}
|
||||
|
||||
public function matchClassWithMethodInstead(string $content): string
|
||||
{
|
||||
$matches = Strings::match($content, self::CLASS_METHOD_INSTEAD_PATTERN);
|
||||
|
||||
return $matches['classMethod'] ?? '';
|
||||
}
|
||||
|
||||
public function matchLocalMethod(string $content): string
|
||||
{
|
||||
$matches = Strings::match($content, '#(?<method>' . self::METHOD_PATTERN . ')#');
|
||||
|
||||
return $matches['classMethod'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Only local namespaced class like:
|
||||
* - "use ContainerBuilder::getReflectionClass() instead"
|
||||
*/
|
||||
public function matchNamespacedClassWithMethod(string $content): string
|
||||
{
|
||||
$matches = Strings::match($content, '#(?<classMethod>[A-Za-z]+::' . self::METHOD_PATTERN . ')#');
|
||||
|
||||
return $matches['classMethod'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function matchMethodArguments(string $method): array
|
||||
{
|
||||
$matches = Strings::match($method, '#\((?<arguments>[^\)]*)\)#');
|
||||
if (! isset($matches['arguments']) || empty($matches['arguments'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$arguments = explode(', ', $matches['arguments']);
|
||||
|
||||
return $this->normalizeMethodArguments($arguments);
|
||||
}
|
||||
|
||||
private function isStringSurroundedBy(string $content, string $needle): bool
|
||||
{
|
||||
return Strings::startsWith($content, $needle) && Strings::endsWith($content, $needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function normalizeMethodArguments(array $arguments): array
|
||||
{
|
||||
foreach ($arguments as $key => $argument) {
|
||||
if ($this->isStringSurroundedBy($argument, '\'')) {
|
||||
$arguments[$key] = Strings::trim($argument, '\'');
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Transformer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassMethodDeprecation;
|
||||
use Rector\DeprecationExtractor\Deprecation\RemovedFunctionalityDeprecation;
|
||||
use Rector\DeprecationExtractor\RegExp\ClassAndMethodMatcher;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Node\Attribute;
|
||||
use Rector\NodeValueResolver\Message\ClassPrepender;
|
||||
use Rector\NodeValueResolver\NodeValueResolver;
|
||||
|
||||
final class ArgumentToDeprecationTransformer
|
||||
{
|
||||
/**
|
||||
* @var ClassAndMethodMatcher
|
||||
*/
|
||||
private $classAndMethodMatcher;
|
||||
|
||||
/**
|
||||
* @var NodeValueResolver
|
||||
*/
|
||||
private $nodeValueResolver;
|
||||
|
||||
public function __construct(
|
||||
ClassAndMethodMatcher $classAndMethodMatcher,
|
||||
NodeValueResolver $nodeValueResolver,
|
||||
ClassPrepender $classPrepender
|
||||
) {
|
||||
$this->classAndMethodMatcher = $classAndMethodMatcher;
|
||||
$this->nodeValueResolver = $nodeValueResolver;
|
||||
$this->classPrepender = $classPrepender;
|
||||
}
|
||||
|
||||
public function transform(Arg $argNode): ?DeprecationInterface
|
||||
{
|
||||
$message = $this->nodeValueResolver->resolve($argNode->value);
|
||||
|
||||
if ($message === null) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$message = $this->classPrepender->completeClassToLocalMethods(
|
||||
$message,
|
||||
(string) $argNode->getAttribute(Attribute::CLASS_NAME)
|
||||
);
|
||||
|
||||
if ($message === '') {
|
||||
throw new NotImplementedException(sprintf(
|
||||
'Not implemented yet. Go to "%s()" and add check for "%s" node.',
|
||||
__METHOD__,
|
||||
get_class($argNode->value)
|
||||
));
|
||||
}
|
||||
|
||||
return $this->createFromMessage($message);
|
||||
}
|
||||
|
||||
public function tryToCreateClassMethodDeprecation(string $oldMessage, string $newMessage): ?DeprecationInterface
|
||||
{
|
||||
$oldMethod = $this->classAndMethodMatcher->matchClassWithMethodWithoutArguments($oldMessage);
|
||||
$newMethod = $this->classAndMethodMatcher->matchClassWithMethodWithoutArguments($newMessage);
|
||||
|
||||
$oldArguments = $this->classAndMethodMatcher->matchMethodArguments($oldMessage);
|
||||
$newArguments = $this->classAndMethodMatcher->matchMethodArguments($newMessage);
|
||||
|
||||
return new ClassMethodDeprecation(
|
||||
$oldMethod,
|
||||
$newMethod,
|
||||
$oldArguments,
|
||||
$newArguments
|
||||
);
|
||||
}
|
||||
|
||||
private function createFromMessage(string $message): DeprecationInterface
|
||||
{
|
||||
$result = Strings::split($message, '#use |Use#');
|
||||
|
||||
if (count($result) === 2) {
|
||||
[$oldMessage, $newMessage] = $result;
|
||||
$deprecation = $this->tryToCreateClassMethodDeprecation($oldMessage, $newMessage);
|
||||
if ($deprecation) {
|
||||
return $deprecation;
|
||||
}
|
||||
}
|
||||
|
||||
return new RemovedFunctionalityDeprecation($message);
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Transformer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\DeprecationExtractor\Contract\Deprecation\DeprecationInterface;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassDeprecation;
|
||||
use Rector\DeprecationExtractor\Deprecation\ClassMethodDeprecation;
|
||||
use Rector\DeprecationExtractor\Deprecation\RemovedClassMethodDeprecation;
|
||||
use Rector\DeprecationExtractor\RegExp\ClassAndMethodMatcher;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Node\Attribute;
|
||||
|
||||
final class MessageToDeprecationTransformer
|
||||
{
|
||||
/**
|
||||
* @var ClassAndMethodMatcher
|
||||
*/
|
||||
private $classAndMethodMatcher;
|
||||
|
||||
public function __construct(ClassAndMethodMatcher $classAndMethodMatcher)
|
||||
{
|
||||
$this->classAndMethodMatcher = $classAndMethodMatcher;
|
||||
}
|
||||
|
||||
public function transform(string $message, Node $node): DeprecationInterface
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
return new ClassDeprecation(
|
||||
$node->namespacedName->toString(),
|
||||
$this->classAndMethodMatcher->matchClassWithMethodInstead($message)
|
||||
);
|
||||
}
|
||||
|
||||
if ($node instanceof ClassMethod) {
|
||||
$classWithMethod = $this->classAndMethodMatcher->matchClassWithMethod($message);
|
||||
$localMethod = $this->classAndMethodMatcher->matchLocalMethod($message);
|
||||
|
||||
$className = $node->getAttribute(Attribute::CLASS_NODE)->namespacedName->toString();
|
||||
$methodName = (string) $node->name . '()';
|
||||
$fqnMethodName = $className . '::' . $methodName;
|
||||
|
||||
if ($classWithMethod === '' && $localMethod === '') {
|
||||
return new RemovedClassMethodDeprecation($className, $methodName);
|
||||
}
|
||||
|
||||
if ($localMethod) {
|
||||
return new ClassMethodDeprecation($fqnMethodName, $className . '::' . $localMethod . '()');
|
||||
}
|
||||
|
||||
$namespacedClassWithMethod = $this->classAndMethodMatcher->matchNamespacedClassWithMethod($message);
|
||||
|
||||
/** @var string[] $useStatements */
|
||||
$useStatements = $node->getAttribute(Attribute::USE_STATEMENTS);
|
||||
$fqnClassWithMethod = $this->completeNamespace($useStatements, $namespacedClassWithMethod);
|
||||
|
||||
return new ClassMethodDeprecation($fqnMethodName, $fqnClassWithMethod);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(sprintf(
|
||||
'%s() was unable to create a Deprecation based on "%s" string and "%s" Node. Create a new method there.',
|
||||
__METHOD__,
|
||||
$message,
|
||||
get_class($node)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $useStatements
|
||||
*/
|
||||
private function completeNamespace(array $useStatements, string $namespacedClassWithMethod): string
|
||||
{
|
||||
[$class, $method] = explode('::', $namespacedClassWithMethod);
|
||||
foreach ($useStatements as $useStatement) {
|
||||
if (Strings::endsWith($useStatement, $class)) {
|
||||
return $useStatement . '::' . $method;
|
||||
}
|
||||
}
|
||||
|
||||
return $namespacedClassWithMethod;
|
||||
}
|
||||
}
|
@ -4,4 +4,4 @@ services:
|
||||
|
||||
Rector\DeprecationExtractor\:
|
||||
resource: '../../src'
|
||||
exclude: '../../src/{Deprecation/*Deprecation.php,Rector/ConfigurableChangeMethodNameRector.php}'
|
||||
exclude: '../../src/{Deprecation/*Deprecation.php,RectorGuess/RectorGuess.php}'
|
||||
|
@ -30,8 +30,6 @@ final class DeprecationCollectorTest extends AbstractContainerAwareTestCase
|
||||
__DIR__ . '/../../../../vendor/symfony/dependency-injection',
|
||||
]);
|
||||
|
||||
$deprecations = $this->deprecationCollector->getDeprecations();
|
||||
|
||||
$this->assertGreaterThanOrEqual(35, $deprecations);
|
||||
$this->assertGreaterThanOrEqual(35, $this->deprecationCollector->getDeprecations());
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace Rector\DeprecationExtractor\Tests;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
|
||||
use Rector\DeprecationExtractor\DeprecationExtractor;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
@ -25,16 +24,7 @@ final class DeprecationExtractorTest extends AbstractContainerAwareTestCase
|
||||
|
||||
public function testDeprectaionMessages(): void
|
||||
{
|
||||
$deprecationMessages = $this->deprecationCollector->getDeprecationMessages();
|
||||
$this->assertCount(0, $deprecationMessages);
|
||||
}
|
||||
|
||||
public function testDeprecationNodes(): void
|
||||
{
|
||||
$deprecationArgNodes = $this->deprecationCollector->getDeprecationArgNodes();
|
||||
$this->assertCount(2, $deprecationArgNodes);
|
||||
|
||||
$deprecationArgNode = $deprecationArgNodes[0];
|
||||
$this->assertInstanceOf(Arg::class, $deprecationArgNode);
|
||||
$deprecationMessages = $this->deprecationCollector->getDeprecations();
|
||||
$this->assertCount(2, $deprecationMessages);
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeprecationExtractor\Tests\Rector;
|
||||
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Rector\DeprecationExtractor\DeprecationExtractor;
|
||||
use Rector\DeprecationExtractor\Rector\RectorFactory;
|
||||
use Rector\Rector\Dynamic\MethodNameReplacerRector;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
|
||||
final class RectorFactoryTest extends AbstractContainerAwareTestCase
|
||||
{
|
||||
/**
|
||||
* @var RectorFactory
|
||||
*/
|
||||
private $rectorFactory;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->rectorFactory = $this->container->get(RectorFactory::class);
|
||||
|
||||
/** @var DeprecationExtractor $deprecationExtractor */
|
||||
$deprecationExtractor = $this->container->get(DeprecationExtractor::class);
|
||||
$deprecationExtractor->scanDirectories([__DIR__ . '/../DeprecationExtractorSource']);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$rectors = $this->rectorFactory->createRectors();
|
||||
$this->assertCount(2, $rectors);
|
||||
|
||||
/** @var MethodNameReplacerRector $secondRector */
|
||||
$secondRector = $rectors[0];
|
||||
$this->assertInstanceOf(MethodNameReplacerRector::class, $secondRector);
|
||||
|
||||
$this->assertSame([
|
||||
'Nette\DI\ServiceDefinition' => [
|
||||
'setInject' => 'addTag',
|
||||
],
|
||||
], Assert::getObjectAttribute($secondRector, 'perClassOldToNewMethods'));
|
||||
|
||||
/** @var MethodNameReplacerRector $firstRector */
|
||||
$firstRector = $rectors[1];
|
||||
$this->assertInstanceOf(MethodNameReplacerRector::class, $firstRector);
|
||||
|
||||
$this->assertSame([
|
||||
'Nette\DI\ServiceDefinition' => [
|
||||
'setClass' => 'setFactory',
|
||||
],
|
||||
], Assert::getObjectAttribute($firstRector, 'perClassOldToNewMethods'));
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeValueResolver\PerNodeValueResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use Rector\NodeValueResolver\Contract\NodeValueResolverAwareInterface;
|
||||
use Rector\NodeValueResolver\Contract\PerNodeValueResolver\PerNodeValueResolverInterface;
|
||||
use Rector\NodeValueResolver\NodeValueResolver;
|
||||
|
||||
final class ArgValueResolver implements PerNodeValueResolverInterface, NodeValueResolverAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeValueResolver
|
||||
*/
|
||||
private $nodeValueResolver;
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return Arg::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Arg $argNode
|
||||
*/
|
||||
public function resolve(Node $argNode): ?string
|
||||
{
|
||||
return $this->nodeValueResolver->resolve($argNode->value);
|
||||
}
|
||||
|
||||
public function setNodeValueResolver(NodeValueResolver $nodeValueResolver): void
|
||||
{
|
||||
$this->nodeValueResolver = $nodeValueResolver;
|
||||
}
|
||||
}
|
@ -4,6 +4,10 @@ namespace Rector\FileSystem;
|
||||
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* Consider using NodeTraverser, same as for namespace to add to node.
|
||||
* Would allow united API.
|
||||
*/
|
||||
final class CurrentFileProvider
|
||||
{
|
||||
/**
|
||||
|
@ -7,7 +7,7 @@ use PhpParser\NodeVisitor;
|
||||
|
||||
final class NodeTraverserFactory
|
||||
{
|
||||
public function createWithNoeVisitor(NodeVisitor $nodeVisitor): NodeTraverser
|
||||
public function createWithNodeVisitor(NodeVisitor $nodeVisitor): NodeTraverser
|
||||
{
|
||||
$nodeTraverser = new NodeTraverser;
|
||||
$nodeTraverser->addVisitor($nodeVisitor);
|
||||
|
@ -38,7 +38,7 @@ final class StandaloneTraverseNodeTraverser
|
||||
public function traverse(array $nodes): array
|
||||
{
|
||||
foreach ($this->nodeVisitors as $nodeVisitor) {
|
||||
$nodeTraverser = $this->nodeTraverserFactory->createWithNoeVisitor($nodeVisitor);
|
||||
$nodeTraverser = $this->nodeTraverserFactory->createWithNodeVisitor($nodeVisitor);
|
||||
$nodes = $nodeTraverser->traverse($nodes);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,11 @@ use Rector\Regex\MagicMethodMatcher;
|
||||
*/
|
||||
final class MagicMethodRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const NETTE_OBJECT_CLASS = 'Nette\Object';
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
@ -36,7 +41,7 @@ final class MagicMethodRector extends AbstractRector
|
||||
/**
|
||||
* @var ClassReflector
|
||||
*/
|
||||
private $currentFileAwareClassReflector;
|
||||
private $classReflector;
|
||||
|
||||
/**
|
||||
* @var MagicMethodMatcher
|
||||
@ -46,12 +51,12 @@ final class MagicMethodRector extends AbstractRector
|
||||
public function __construct(
|
||||
MethodBuilder $methodBuilder,
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
ClassReflector $currentFileAwareClassReflector,
|
||||
ClassReflector $classReflector,
|
||||
MagicMethodMatcher $magicMethodMatcher
|
||||
) {
|
||||
$this->methodBuilder = $methodBuilder;
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->currentFileAwareClassReflector = $currentFileAwareClassReflector;
|
||||
$this->classReflector = $classReflector;
|
||||
$this->magicMethodMatcher = $magicMethodMatcher;
|
||||
}
|
||||
|
||||
@ -77,7 +82,7 @@ final class MagicMethodRector extends AbstractRector
|
||||
$className = $node->getAttribute(Attribute::CLASS_NAME);
|
||||
|
||||
$this->magicMethods = $this->magicMethodMatcher->matchInContent(
|
||||
$this->currentFileAwareClassReflector->reflect($className),
|
||||
$this->classReflector->reflect($className),
|
||||
$docComments[0]->getText()
|
||||
);
|
||||
|
||||
@ -116,6 +121,6 @@ final class MagicMethodRector extends AbstractRector
|
||||
|
||||
$parentClassName = (string) $classNode->extends->getAttribute(Attribute::RESOLVED_NAME);
|
||||
|
||||
return $parentClassName === 'Nette\Object';
|
||||
return $parentClassName === self::NETTE_OBJECT_CLASS;
|
||||
}
|
||||
}
|
||||
|
27
src/Rector/Dynamic/MethodArgumentChangerRector.php
Normal file
27
src/Rector/Dynamic/MethodArgumentChangerRector.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Rector\Dynamic;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\Rector\AbstractRector;
|
||||
|
||||
/**
|
||||
* @todo collect cases and prepare tests for them
|
||||
*
|
||||
* Possible options so far:
|
||||
* - new argument
|
||||
* - argument removed
|
||||
* - new default value for argument
|
||||
*/
|
||||
final class MethodArgumentChangerRector extends AbstractRector
|
||||
{
|
||||
public function isCandidate(Node $node): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user