diff --git a/composer.json b/composer.json index 8c32b9efec8..c6980f2e9ae 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/easy-coding-standard.neon b/easy-coding-standard.neon index e61bf4e0e09..4753b7569ab 100644 --- a/easy-coding-standard.neon +++ b/easy-coding-standard.neon @@ -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 diff --git a/packages/DeprecationExtractor/README.md b/packages/DeprecationExtractor/README.md index cf566322acf..d3c63886c2d 100644 --- a/packages/DeprecationExtractor/README.md +++ b/packages/DeprecationExtractor/README.md @@ -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. diff --git a/packages/DeprecationExtractor/src/Console/Command/ExtractDeprecationsCommand.php b/packages/DeprecationExtractor/src/Console/Command/ExtractDeprecationsCommand.php index d7d9fcfb9c0..30288b3cd63 100644 --- a/packages/DeprecationExtractor/src/Console/Command/ExtractDeprecationsCommand.php +++ b/packages/DeprecationExtractor/src/Console/Command/ExtractDeprecationsCommand.php @@ -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; diff --git a/packages/DeprecationExtractor/src/Contract/Deprecation/DeprecationInterface.php b/packages/DeprecationExtractor/src/Contract/Deprecation/DeprecationInterface.php deleted file mode 100644 index 8f17cc54ef9..00000000000 --- a/packages/DeprecationExtractor/src/Contract/Deprecation/DeprecationInterface.php +++ /dev/null @@ -1,7 +0,0 @@ -oldClass = $oldClass; - $this->newClass = $newClass; - } - - public function getOldClass(): string - { - return $this->oldClass; - } - - public function getNewClass(): string - { - return $this->newClass; - } -} diff --git a/packages/DeprecationExtractor/src/Deprecation/ClassMethodDeprecation.php b/packages/DeprecationExtractor/src/Deprecation/ClassMethodDeprecation.php deleted file mode 100644 index 1b89ca628fd..00000000000 --- a/packages/DeprecationExtractor/src/Deprecation/ClassMethodDeprecation.php +++ /dev/null @@ -1,105 +0,0 @@ -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; - } -} diff --git a/packages/DeprecationExtractor/src/Deprecation/Deprecation.php b/packages/DeprecationExtractor/src/Deprecation/Deprecation.php new file mode 100644 index 00000000000..d09fbe91be6 --- /dev/null +++ b/packages/DeprecationExtractor/src/Deprecation/Deprecation.php @@ -0,0 +1,39 @@ +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; + } +} diff --git a/packages/DeprecationExtractor/src/Deprecation/DeprecationCollector.php b/packages/DeprecationExtractor/src/Deprecation/DeprecationCollector.php index dc2425c0aad..c1764ad4b1b 100644 --- a/packages/DeprecationExtractor/src/Deprecation/DeprecationCollector.php +++ b/packages/DeprecationExtractor/src/Deprecation/DeprecationCollector.php @@ -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); - } - } - } } diff --git a/packages/DeprecationExtractor/src/Deprecation/RemovedClassMethodDeprecation.php b/packages/DeprecationExtractor/src/Deprecation/RemovedClassMethodDeprecation.php deleted file mode 100644 index eee1d870087..00000000000 --- a/packages/DeprecationExtractor/src/Deprecation/RemovedClassMethodDeprecation.php +++ /dev/null @@ -1,34 +0,0 @@ -class = $class; - $this->method = $method; - } - - public function getClass(): string - { - return $this->class; - } - - public function getMethod(): string - { - return $this->method; - } -} diff --git a/packages/DeprecationExtractor/src/Deprecation/RemovedFunctionalityDeprecation.php b/packages/DeprecationExtractor/src/Deprecation/RemovedFunctionalityDeprecation.php deleted file mode 100644 index d25c7f76088..00000000000 --- a/packages/DeprecationExtractor/src/Deprecation/RemovedFunctionalityDeprecation.php +++ /dev/null @@ -1,23 +0,0 @@ -message = $message; - } - - public function getMessage(): string - { - return $this->message; - } -} diff --git a/packages/DeprecationExtractor/src/DeprecationExtractor.php b/packages/DeprecationExtractor/src/DeprecationExtractor.php index e2e15541899..6310267aaf8 100644 --- a/packages/DeprecationExtractor/src/DeprecationExtractor.php +++ b/packages/DeprecationExtractor/src/DeprecationExtractor.php @@ -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()); - } } diff --git a/packages/DeprecationExtractor/src/NodeAnalyzer/TriggerErrorAnalyzer.php b/packages/DeprecationExtractor/src/NodeAnalyzer/TriggerErrorAnalyzer.php index 74541d59384..c949df54179 100644 --- a/packages/DeprecationExtractor/src/NodeAnalyzer/TriggerErrorAnalyzer.php +++ b/packages/DeprecationExtractor/src/NodeAnalyzer/TriggerErrorAnalyzer.php @@ -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 { diff --git a/packages/DeprecationExtractor/src/NodeVisitor/DeprecationDetector.php b/packages/DeprecationExtractor/src/NodeVisitor/DeprecationDetector.php index dbb2c1f81ce..95a500a90a0 100644 --- a/packages/DeprecationExtractor/src/NodeVisitor/DeprecationDetector.php +++ b/packages/DeprecationExtractor/src/NodeVisitor/DeprecationDetector.php @@ -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); } } diff --git a/packages/DeprecationExtractor/src/Rector/RectorFactory.php b/packages/DeprecationExtractor/src/Rector/RectorFactory.php deleted file mode 100644 index c080118f1ad..00000000000 --- a/packages/DeprecationExtractor/src/Rector/RectorFactory.php +++ /dev/null @@ -1,62 +0,0 @@ -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) - )); - } -} diff --git a/packages/DeprecationExtractor/src/Rector/RectorGuesser.php b/packages/DeprecationExtractor/src/Rector/RectorGuesser.php new file mode 100644 index 00000000000..597c0f935a0 --- /dev/null +++ b/packages/DeprecationExtractor/src/Rector/RectorGuesser.php @@ -0,0 +1,81 @@ +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()); + } +} diff --git a/packages/DeprecationExtractor/src/Rector/UnsupportedDeprecationFilter.php b/packages/DeprecationExtractor/src/Rector/UnsupportedDeprecationFilter.php new file mode 100644 index 00000000000..ab3827ef032 --- /dev/null +++ b/packages/DeprecationExtractor/src/Rector/UnsupportedDeprecationFilter.php @@ -0,0 +1,52 @@ +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; + } +} diff --git a/packages/DeprecationExtractor/src/RectorGuess/RectorGuess.php b/packages/DeprecationExtractor/src/RectorGuess/RectorGuess.php new file mode 100644 index 00000000000..f86465736a7 --- /dev/null +++ b/packages/DeprecationExtractor/src/RectorGuess/RectorGuess.php @@ -0,0 +1,63 @@ +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); + } +} diff --git a/packages/DeprecationExtractor/src/RectorGuess/RectorGuessFactory.php b/packages/DeprecationExtractor/src/RectorGuess/RectorGuessFactory.php new file mode 100644 index 00000000000..0db97ea5f40 --- /dev/null +++ b/packages/DeprecationExtractor/src/RectorGuess/RectorGuessFactory.php @@ -0,0 +1,36 @@ + instead - * - Use the instead - * - Use the class instead - * - use the class instead - * - * @var string - */ - private const CLASS_METHOD_INSTEAD_PATTERN = '#use( the)? (?' . - self::CLASS_PATTERN . - ')( class)? instead#i'; - - /** - * @var string - */ - private const CLASS_METHOD_PATTERN = '#^(?' . - self::CLASS_PATTERN . - '::' . - self::METHOD_PATTERN . - ')#s'; - - /** - * @var string - */ - private const CLASS_METHOD_WITHOUT_ARGUMENTS_PATTERN = '#^(?' . - 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, '#(?' . 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, '#(?[A-Za-z]+::' . self::METHOD_PATTERN . ')#'); - - return $matches['classMethod'] ?? ''; - } - - /** - * @return mixed[] - */ - public function matchMethodArguments(string $method): array - { - $matches = Strings::match($method, '#\((?[^\)]*)\)#'); - 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; - } -} diff --git a/packages/DeprecationExtractor/src/Transformer/ArgumentToDeprecationTransformer.php b/packages/DeprecationExtractor/src/Transformer/ArgumentToDeprecationTransformer.php deleted file mode 100644 index b2613d186bb..00000000000 --- a/packages/DeprecationExtractor/src/Transformer/ArgumentToDeprecationTransformer.php +++ /dev/null @@ -1,92 +0,0 @@ -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); - } -} diff --git a/packages/DeprecationExtractor/src/Transformer/MessageToDeprecationTransformer.php b/packages/DeprecationExtractor/src/Transformer/MessageToDeprecationTransformer.php deleted file mode 100644 index bd01b5c1e89..00000000000 --- a/packages/DeprecationExtractor/src/Transformer/MessageToDeprecationTransformer.php +++ /dev/null @@ -1,85 +0,0 @@ -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; - } -} diff --git a/packages/DeprecationExtractor/src/config/services.yml b/packages/DeprecationExtractor/src/config/services.yml index 3de87748c33..c086e1451d9 100644 --- a/packages/DeprecationExtractor/src/config/services.yml +++ b/packages/DeprecationExtractor/src/config/services.yml @@ -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}' diff --git a/packages/DeprecationExtractor/tests/Deprecation/DeprecationCollectorTest.php b/packages/DeprecationExtractor/tests/Deprecation/DeprecationCollectorTest.php index 3170b050b01..397aecb271c 100644 --- a/packages/DeprecationExtractor/tests/Deprecation/DeprecationCollectorTest.php +++ b/packages/DeprecationExtractor/tests/Deprecation/DeprecationCollectorTest.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()); } } diff --git a/packages/DeprecationExtractor/tests/DeprecationExtractorTest.php b/packages/DeprecationExtractor/tests/DeprecationExtractorTest.php index 26e655391aa..fe86b536692 100644 --- a/packages/DeprecationExtractor/tests/DeprecationExtractorTest.php +++ b/packages/DeprecationExtractor/tests/DeprecationExtractorTest.php @@ -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); } } diff --git a/packages/DeprecationExtractor/tests/Rector/RectorFactoryTest.php b/packages/DeprecationExtractor/tests/Rector/RectorFactoryTest.php deleted file mode 100644 index 7eeeed440f7..00000000000 --- a/packages/DeprecationExtractor/tests/Rector/RectorFactoryTest.php +++ /dev/null @@ -1,52 +0,0 @@ -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')); - } -} diff --git a/packages/NodeValueResolver/src/PerNodeValueResolver/ArgValueResolver.php b/packages/NodeValueResolver/src/PerNodeValueResolver/ArgValueResolver.php new file mode 100644 index 00000000000..8b1e855bd12 --- /dev/null +++ b/packages/NodeValueResolver/src/PerNodeValueResolver/ArgValueResolver.php @@ -0,0 +1,35 @@ +nodeValueResolver->resolve($argNode->value); + } + + public function setNodeValueResolver(NodeValueResolver $nodeValueResolver): void + { + $this->nodeValueResolver = $nodeValueResolver; + } +} diff --git a/src/FileSystem/CurrentFileProvider.php b/src/FileSystem/CurrentFileProvider.php index 2e3569c06b2..bbe65900782 100644 --- a/src/FileSystem/CurrentFileProvider.php +++ b/src/FileSystem/CurrentFileProvider.php @@ -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 { /** diff --git a/src/NodeTraverser/NodeTraverserFactory.php b/src/NodeTraverser/NodeTraverserFactory.php index 691bd92fbd0..0e6f6af1c30 100644 --- a/src/NodeTraverser/NodeTraverserFactory.php +++ b/src/NodeTraverser/NodeTraverserFactory.php @@ -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); diff --git a/src/NodeTraverser/StandaloneTraverseNodeTraverser.php b/src/NodeTraverser/StandaloneTraverseNodeTraverser.php index 77259cc0ba2..5731ad8fcd7 100644 --- a/src/NodeTraverser/StandaloneTraverseNodeTraverser.php +++ b/src/NodeTraverser/StandaloneTraverseNodeTraverser.php @@ -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); } diff --git a/src/Rector/Contrib/Nette/Utils/MagicMethodRector.php b/src/Rector/Contrib/Nette/Utils/MagicMethodRector.php index c5cd924afb6..76873dea8de 100644 --- a/src/Rector/Contrib/Nette/Utils/MagicMethodRector.php +++ b/src/Rector/Contrib/Nette/Utils/MagicMethodRector.php @@ -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; } } diff --git a/src/Rector/Dynamic/MethodArgumentChangerRector.php b/src/Rector/Dynamic/MethodArgumentChangerRector.php new file mode 100644 index 00000000000..281096d1344 --- /dev/null +++ b/src/Rector/Dynamic/MethodArgumentChangerRector.php @@ -0,0 +1,27 @@ +