diff --git a/README.md b/README.md index f19ffb982ff..6c337dac86b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ This tool will *reconstruct* (change) your code - **run it only in a new clean g ## All Reconstructors -- `InjectAnnotationToConstructorReconstructor` +- `InjectAnnotationToConstructorNodeTraverser` ([Nette](https://github.com/nette/)) +- `NamedServicesToConstructorNodeTraverser` ([Symfony](https://github.com/symfony/)) ## Install diff --git a/composer.json b/composer.json index 087c4a86e65..45bd0db6e65 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,14 @@ "php": "^7.1", "symfony/console": "^3.3", "symfony/dependency-injection": "^3.3", - "nikic/php-parser": "^3.0", - "ocramius/code-generator-utils": "^0.4.1" + "nikic/php-parser": "4.0.x-dev as 3.0.2", + "ocramius/code-generator-utils": "^0.4", + "nette/utils": "^2.4" }, "require-dev": { "phpunit/phpunit": "^6.2", "tracy/tracy": "^2.4", - "symplify/easy-coding-standard": "^2.1", + "symplify/easy-coding-standard": "@dev", "phpstan/phpstan": "^0.7" }, "autoload": { @@ -33,6 +34,6 @@ "all": ["phpunit", "@cs", "@ps"], "cs": "ecs check src tests", "fs": "ecs check src tests --fix", - "ps": "phpstan analyse src tests --level 7 -c phpstan.neon" + "ps": "phpstan analyse src tests --level 7 --configuration phpstan.neon" } } diff --git a/easy-coding-standard.neon b/easy-coding-standard.neon new file mode 100644 index 00000000000..b90bf2b0190 --- /dev/null +++ b/easy-coding-standard.neon @@ -0,0 +1,3 @@ +includes: +# todo: +# - vendor/symplify/easy-coding-standard/config/ \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon index d980c0bf12f..b7f7220cbe1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,4 @@ parameters: ignoreErrors: - - '#should return PhpParser\\Node\[\] but returns PhpParser\\Node\[\]\|null#' - # annotated => bug - - '#Access to an undefined property PhpParser\\Node\\Expr::\$value#' + - '#Internal error#' +# - '#should return PhpParser\\Node\[\] but returns PhpParser\\Node\[\]\|null#' diff --git a/src/Application/FileProcessor.php b/src/Application/FileProcessor.php index ed46836fd61..45e31f70d56 100644 --- a/src/Application/FileProcessor.php +++ b/src/Application/FileProcessor.php @@ -2,8 +2,9 @@ namespace Rector\Application; +use PhpParser\Lexer; +use PhpParser\NodeTraverser; use PhpParser\Parser; -use Rector\Dispatcher\NodeDispatcher; use Rector\Printer\CodeStyledPrinter; use SplFileInfo; @@ -14,21 +15,27 @@ final class FileProcessor */ private $parser; - /** - * @var NodeDispatcher - */ - private $nodeDispatcher; - /** * @var CodeStyledPrinter */ private $codeStyledPrinter; - public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter, NodeDispatcher $nodeDispatcher) + /** + * @var NodeTraverser + */ + private $nodeTraverser; + + /** + * @var Lexer + */ + private $lexer; + + public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter, Lexer $lexer, NodeTraverser $nodeTraverser) { $this->parser = $parser; - $this->nodeDispatcher = $nodeDispatcher; $this->codeStyledPrinter = $codeStyledPrinter; + $this->nodeTraverser = $nodeTraverser; + $this->lexer = $lexer; } /** @@ -44,18 +51,18 @@ final class FileProcessor public function processFile(SplFileInfo $file): void { $fileContent = file_get_contents($file->getRealPath()); - $nodes = $this->parser->parse($fileContent); - if ($nodes === null) { + $oldStmts = $this->parser->parse($fileContent); + if ($oldStmts === null) { return; } - $originalNodes = $this->cloneArrayOfObjects($nodes); + $oldStmts = $this->cloneArrayOfObjects($oldStmts); - foreach ($nodes as $node) { - $this->nodeDispatcher->dispatch($node); - } + $oldTokens = $this->lexer->getTokens(); - $this->codeStyledPrinter->printToFile($file, $originalNodes, $nodes); + $newStmts = $this->nodeTraverser->traverse($oldStmts); + + $this->codeStyledPrinter->printToFile($file, $newStmts, $oldStmts, $oldTokens); } /** diff --git a/src/Builder/Class_/ClassPropertyCollector.php b/src/Builder/Class_/ClassPropertyCollector.php new file mode 100644 index 00000000000..374a9d03ef9 --- /dev/null +++ b/src/Builder/Class_/ClassPropertyCollector.php @@ -0,0 +1,26 @@ +classProperties[$class] = [ + $propertyType => $propertyName + ]; + } + + /** + * @return string[] + */ + public function getPropertiesforClass(string $class): array + { + return $this->classProperties[$class] ?? []; + } +} diff --git a/src/Builder/ConstructorMethodBuilder.php b/src/Builder/ConstructorMethodBuilder.php index 1ae7ac95490..1f4ddd8a6fe 100644 --- a/src/Builder/ConstructorMethodBuilder.php +++ b/src/Builder/ConstructorMethodBuilder.php @@ -2,6 +2,7 @@ namespace Rector\Builder; +use Nette\Utils\Arrays; use PhpParser\Builder\Method; use PhpParser\Builder\Param; use PhpParser\BuilderFactory; @@ -49,7 +50,7 @@ final class ConstructorMethodBuilder ->addParam($this->createParameter($propertyType, $propertyName)) ->addStmts($assign); - $classNode->stmts[] = $constructorMethod->getNode(); + $this->addAsFirstMethod($classNode, $constructorMethod->getNode()); } private function createParameter(string $propertyType, string $propertyName): Param @@ -69,4 +70,21 @@ final class ConstructorMethodBuilder $propertyName )); } -} \ No newline at end of file + + private function addAsFirstMethod(Class_ $classNode, ClassMethod $constructorMethod): void + { + foreach ($classNode->stmts as $key => $classElementNode) { + if ($classElementNode instanceof ClassMethod) { + Arrays::insertBefore( + $classNode->stmts, + $key, + ['before_' . $key => $constructorMethod] + ); + + return; + } + } + + $classNode->stmts[] = $constructorMethod; + } +} diff --git a/src/Builder/Kernel/ServiceFromKernelResolver.php b/src/Builder/Kernel/ServiceFromKernelResolver.php new file mode 100644 index 00000000000..4933b9661c9 --- /dev/null +++ b/src/Builder/Kernel/ServiceFromKernelResolver.php @@ -0,0 +1,29 @@ +boot(); + + // @todo: cache + // @todo: initialize without creating cache or log directory + // @todo: call only loadBundles() and initializeContainer() methods + + $container = $kernel->getContainer(); + if (! $container->has($serviceName)) { + // service name could not be found + return null; + } + + $service = $container->get($serviceName); + + return get_class($service); + } +} diff --git a/src/Builder/Naming/NameResolver.php b/src/Builder/Naming/NameResolver.php new file mode 100644 index 00000000000..6c89b8eff2b --- /dev/null +++ b/src/Builder/Naming/NameResolver.php @@ -0,0 +1,14 @@ +builderFactory = $builderFactory; + } + + public function addPropertyToClass(Class_ $classNode, string $propertyType, string $propertyName): void + { + $propertyNode = $this->buildPrivatePropertyNode($propertyType, $propertyName); + + // add before first method + foreach ($classNode->stmts as $key => $classElementNode) { + if ($classElementNode instanceof ClassMethod) { + Arrays::insertBefore( + $classNode->stmts, + $key, + ['before_' . $key => $propertyNode] + ); + + return; + } + } + + // or after last property + $previousElement = null; + foreach ($classNode->stmts as $key => $classElementNode) { + if ($previousElement instanceof Property && ! $classElementNode instanceof Property) { + Arrays::insertBefore( + $classNode->stmts, + $key, + ['before_' . $key => $propertyNode] + ); + + return; + } + + $previousElement = $classElementNode; + } + + $classNode->stmts[] = $propertyNode; + } + + private function buildPrivatePropertyNode(string $propertyType, string $propertyName): Property + { + $docComment = $this->createDocWithVarAnnotation($propertyType); + + $propertyBuilder = $this->builderFactory->property($propertyName) + ->makePrivate() + ->setDocComment($docComment); + + return $propertyBuilder->getNode(); + } + + private function createDocWithVarAnnotation(string $propertyType): Doc + { + return new Doc('/**' + . PHP_EOL . ' * @var ' . $propertyType + . PHP_EOL . ' */'); + } +} diff --git a/src/Console/Command/ReconstructCommand.php b/src/Console/Command/ReconstructCommand.php index 5120e92f9fb..6284b10bde9 100644 --- a/src/Console/Command/ReconstructCommand.php +++ b/src/Console/Command/ReconstructCommand.php @@ -38,8 +38,6 @@ final class ReconstructCommand extends Command { $this->setName(self::NAME); $this->setDescription('Reconstruct set of your code.'); - - // @todo: use modular configure from ApiGen $this->addArgument( self::ARGUMENT_SOURCE_NAME, InputArgument::REQUIRED | InputArgument::IS_ARRAY, diff --git a/src/Contract/Dispatcher/ReconstructorInterface.php b/src/Contract/Dispatcher/ReconstructorInterface.php deleted file mode 100644 index 9cf20f0b421..00000000000 --- a/src/Contract/Dispatcher/ReconstructorInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -collectCommandsToConsoleApplication($containerBuilder); - $this->collectReconstructorsToNodeDispatcher($containerBuilder); + $this->collectNodeVisitorsToTraverser($containerBuilder); } private function collectCommandsToConsoleApplication(ContainerBuilder $containerBuilder): void @@ -28,13 +28,13 @@ final class CollectorCompilerPass implements CompilerPassInterface ); } - private function collectReconstructorsToNodeDispatcher(ContainerBuilder $containerBuilder): void + private function collectNodeVisitorsToTraverser(ContainerBuilder $containerBuilder): void { DefinitionCollector::loadCollectorWithType( $containerBuilder, - NodeDispatcher::class, - ReconstructorInterface::class, - 'addReconstructor' + NodeTraverser::class, + NodeVisitor::class, + 'addVisitor' ); } } diff --git a/src/Dispatcher/NodeDispatcher.php b/src/Dispatcher/NodeDispatcher.php deleted file mode 100644 index 97744043e24..00000000000 --- a/src/Dispatcher/NodeDispatcher.php +++ /dev/null @@ -1,29 +0,0 @@ -reconstructors[] = $reconstructor; - } - - public function dispatch(Node $node): void - { - // todo: build hash map - foreach ($this->reconstructors as $reconstructor) { - if ($reconstructor->isCandidate($node)) { - $reconstructor->reconstruct($node); - } - } - } -} diff --git a/src/NodeTraverser/StateHolder.php b/src/NodeTraverser/StateHolder.php new file mode 100644 index 00000000000..ace836cd553 --- /dev/null +++ b/src/NodeTraverser/StateHolder.php @@ -0,0 +1,22 @@ +isAfterTraverseCalled = true; + } + + public function isAfterTraverseCalled(): bool + { + return $this->isAfterTraverseCalled; + } +} + diff --git a/src/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor.php b/src/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorNodeVisitor.php similarity index 65% rename from src/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor.php rename to src/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorNodeVisitor.php index 8307ea65d6d..710419938b3 100644 --- a/src/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor.php +++ b/src/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorNodeVisitor.php @@ -1,17 +1,22 @@ $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value + * + * @return null|int|Node Replacement node (or special return value) */ - public function reconstruct(Node $classNode): void + public function enterNode(Node $node) + { + if ($node instanceof Class_) { + $this->reconstruct($node); + return $node; + } + + return null; + } + + private function reconstruct(Class_ $classNode): void { foreach ($classNode->stmts as $classElementStatement) { if (! $classElementStatement instanceof Property) { @@ -39,7 +66,7 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI $propertyNode = $classElementStatement; $propertyDocBlock = $this->createDocBlockFromProperty($propertyNode); - $injectAnnotations = $propertyDocBlock->getAnnotationsOfType('inject'); + $injectAnnotations = $propertyDocBlock->getAnnotationsOfType(self::ANNOTATION_INJECT); if (! $injectAnnotations) { continue; } @@ -49,7 +76,8 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI $propertyType = $propertyDocBlock->getAnnotationsOfType('var')[0] ->getTypes()[0]; - $propertyName = $propertyNode->props[0]->name; + $propertyName = (string) $propertyNode->props[0]->name; + $this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName); } } @@ -66,7 +94,7 @@ final class InjectAnnotationToConstructorReconstructor implements ReconstructorI private function removeInjectAnnotationFromProperty(Property $propertyNode, DocBlock $propertyDocBlock): void { - $injectAnnotations = $propertyDocBlock->getAnnotationsOfType('inject'); + $injectAnnotations = $propertyDocBlock->getAnnotationsOfType(self::ANNOTATION_INJECT); foreach ($injectAnnotations as $injectAnnotation) { $injectAnnotation->remove(); diff --git a/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/AddPropertiesToClassNodeVisitor.php b/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/AddPropertiesToClassNodeVisitor.php new file mode 100644 index 00000000000..0b9fbfd2b99 --- /dev/null +++ b/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/AddPropertiesToClassNodeVisitor.php @@ -0,0 +1,76 @@ +constructorMethodBuilder = $constructorMethodBuilder; + $this->propertyBuilder = $propertyBuilder; + $this->newClassPropertyCollector = $newClassPropertyCollector; + $this->stateHolder = $stateHolder; + } + + /** + * @param Node[] $nodes + * @return Node[] + */ + public function afterTraverse(array $nodes): array + { + foreach ($nodes as $node) { + if ($node instanceof Class_) { + $this->reconstruct($node, (string) $node->name); + break; + } + } + + return $nodes; + } + + private function reconstruct(Class_ $classNode, string $className): void + { + $propertiesForClass = $this->newClassPropertyCollector->getPropertiesforClass($className); + + foreach ($propertiesForClass as $propertyType => $propertyName) { + $this->stateHolder->setAfterTraverserIsCalled(); + $this->constructorMethodBuilder->addPropertyAssignToClass($classNode, $propertyType, $propertyName); + $this->propertyBuilder->addPropertyToClass($classNode, $propertyType, $propertyName); + } + } +} diff --git a/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/GetterToPropertyNodeVisitor.php b/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/GetterToPropertyNodeVisitor.php new file mode 100644 index 00000000000..d967af595fd --- /dev/null +++ b/src/NodeVisitor/DependencyInjection/NamedServicesToConstructor/GetterToPropertyNodeVisitor.php @@ -0,0 +1,190 @@ +get('some_service') # where "some_service" is name of the service in container + * + * into: + * $this->someService # where "someService" is type of the service + */ +final class GetterToPropertyNodeVisitor extends NodeVisitorAbstract +{ + /** + * @var string + */ + private $className; + + /** + * @var NameResolver + */ + private $nameResolver; + + /** + * @var ServiceFromKernelResolver + */ + private $serviceFromKernelResolver; + + /** + * @var ClassPropertyCollector + */ + private $classPropertyCollector; + + public function __construct( + NameResolver $nameResolver, + ServiceFromKernelResolver $serviceFromKernelResolver, + ClassPropertyCollector $classPropertyCollector + ) { + $this->nameResolver = $nameResolver; + $this->serviceFromKernelResolver = $serviceFromKernelResolver; + $this->classPropertyCollector = $classPropertyCollector; + } + + /** + * @param Node[] $nodes + * @return array|null + */ + public function beforeTraverse(array $nodes): ?array + { + foreach ($nodes as $node) { + if ($node instanceof Node\Stmt\Class_) { + $this->className = (string) $node->name; + } + } + + return null; + } + + /** + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value + * + * @return null|int|Node + */ + public function enterNode(Node $node) + { + if ($this->isCandidate($node)) { + $this->reconstruct($node); + return $node; + } + + return null; + } + + private function isCandidate(Node $node): bool + { + // $var = $this->get('some_service'); + // $var = $this->get('some_service')->getData(); + if ($node instanceof Assign) { + if ($node->expr instanceof MethodCall || $node->var instanceof MethodCall) { + if ($this->isContainerGetCall($node->expr)) { + return true; + } + } + } + + // ['var => $this->get('some_service')->getData()] + if ($node instanceof MethodCall && $node->var instanceof MethodCall) { + if ($this->isContainerGetCall($node->var)) { + return true; + } + } + + return false; + } + + /** + * @param Assign|MethodCall $assignOrMethodCallNode + */ + private function reconstruct(Node $assignOrMethodCallNode): void + { + if ($assignOrMethodCallNode instanceof Assign) { + $refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->expr); + if ($refactoredMethodCall) { + $assignOrMethodCallNode->expr = $refactoredMethodCall; + } + } + + if ($assignOrMethodCallNode instanceof MethodCall) { + $refactoredMethodCall = $this->processMethodCallNode($assignOrMethodCallNode->var); + if ($refactoredMethodCall) { + $assignOrMethodCallNode->var = $refactoredMethodCall; + } + } + } + + /** + * Is "$this->get('string')" statements? + */ + private function isContainerGetCall(MethodCall $methodCall): bool + { + if ($methodCall->var->name !== 'this') { + return false; + } + + if ((string) $methodCall->name !== 'get') { + return false; + } + + if (! $methodCall->args[0]->value instanceof String_) { + return false; + } + + return true; + } + + private function processMethodCallNode(MethodCall $methodCall): ?PropertyFetch + { + /** @var String_ $argument */ + $argument = $methodCall->args[0]->value; + $serviceName = $argument->value; + + $serviceType = $this->serviceFromKernelResolver->resolveServiceClassByNameFromKernel( + $serviceName, LocalKernel::class + ); + + if ($serviceType === null) { + return null; + } + + + $propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType); + + $this->classPropertyCollector->addPropertyForClass($this->className, $serviceType, $propertyName); + + return $this->createPropertyFetch($propertyName); + + } + + /** + * Creates "$this->propertyName" + */ + private function createPropertyFetch(string $propertyName): PropertyFetch + { + return new PropertyFetch( + new Variable('this', [ + 'name' => $propertyName + ]), $propertyName + ); + } +} diff --git a/src/Parser/LexerFactory.php b/src/Parser/LexerFactory.php new file mode 100644 index 00000000000..f6906f3c51e --- /dev/null +++ b/src/Parser/LexerFactory.php @@ -0,0 +1,24 @@ + [ + 'comments', + 'startLine', 'endLine', + 'startTokenPos', 'endTokenPos', + ], + ]); + } +} diff --git a/src/Parser/ParserFactory.php b/src/Parser/ParserFactory.php index af706005932..8ad27b9b9d0 100644 --- a/src/Parser/ParserFactory.php +++ b/src/Parser/ParserFactory.php @@ -2,13 +2,25 @@ namespace Rector\Parser; +use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory as NikicParserFactory; final class ParserFactory { + /** + * @var Lexer + */ + private $lexer; + + public function __construct(Lexer $lexer) + { + $this->lexer = $lexer; + } + public function create(): Parser { - return (new NikicParserFactory)->create(NikicParserFactory::PREFER_PHP7); + $nikicParserFactory = new NikicParserFactory; + return $nikicParserFactory->create(NikicParserFactory::PREFER_PHP7, $this->lexer); } } diff --git a/src/Printer/CodeStyledPrinter.php b/src/Printer/CodeStyledPrinter.php index c2ffee1be8a..08ea5addb20 100644 --- a/src/Printer/CodeStyledPrinter.php +++ b/src/Printer/CodeStyledPrinter.php @@ -17,18 +17,18 @@ final class CodeStyledPrinter $this->prettyPrinter = $prettyPrinter; } - public function printToFile(SplFileInfo $file, array $originalNodes, array $newNodes): void + public function printToFile(SplFileInfo $file, array $newStmts, array $oldStmts, array $oldTokens): void { - if ($originalNodes === $newNodes) { + if ($oldStmts === $newStmts) { return; } - file_put_contents($file->getRealPath(), $this->printToString($newNodes)); + file_put_contents($file->getRealPath(), $this->printToString($newStmts, $oldStmts, $oldTokens)); // @todo: run ecs with minimal set to code look nice } - public function printToString(array $newNodes): string + public function printToString(array $newStmts, array $oldStmts, array $oldTokens): string { - return 'prettyPrinter->prettyPrint($newNodes); + return $this->prettyPrinter->printFormatPreserving($newStmts, $oldStmts, $oldTokens); } } diff --git a/src/Testing/Application/FileReconstructor.php b/src/Testing/Application/FileReconstructor.php index b126d3a906e..5acd3feb141 100644 --- a/src/Testing/Application/FileReconstructor.php +++ b/src/Testing/Application/FileReconstructor.php @@ -2,9 +2,10 @@ namespace Rector\Testing\Application; -use PhpParser\Node; +use PhpParser\Lexer; +use PhpParser\NodeTraverser; use PhpParser\Parser; -use Rector\Contract\Dispatcher\ReconstructorInterface; +use Rector\NodeTraverser\StateHolder; use Rector\Printer\CodeStyledPrinter; use SplFileInfo; @@ -20,25 +21,48 @@ final class FileReconstructor */ private $codeStyledPrinter; - public function __construct(Parser $parser, CodeStyledPrinter $codeStyledPrinter) - { + /** + * @var Lexer + */ + private $lexer; + + /** + * @var NodeTraverser + */ + private $nodeTraverser; + + /** + * @var StateHolder + */ + private $stateHolder; + + public function __construct( + Parser $parser, + CodeStyledPrinter $codeStyledPrinter, + Lexer $lexer, + NodeTraverser $nodeTraverser, + StateHolder $stateHolder + ) { $this->parser = $parser; $this->codeStyledPrinter = $codeStyledPrinter; + $this->lexer = $lexer; + $this->nodeTraverser = $nodeTraverser; + $this->stateHolder = $stateHolder; } - public function processFileWithReconstructor(SplFileInfo $file, ReconstructorInterface $reconstructor): string + # ref: https://github.com/nikic/PHP-Parser/issues/344#issuecomment-298162516 + public function processFile(SplFileInfo $file): string { $fileContent = file_get_contents($file->getRealPath()); - /** @var Node[] $nodes */ - $nodes = $this->parser->parse($fileContent); + $oldStmts = $this->parser->parse($fileContent); + $oldTokens = $this->lexer->getTokens(); + $newStmts = $this->nodeTraverser->traverse($oldStmts); - foreach ($nodes as $node) { - if ($reconstructor->isCandidate($node)) { - $reconstructor->reconstruct($node); - } + if (! $this->stateHolder->isAfterTraverseCalled()) { + [$newStmts, $oldStmts] = [$oldStmts, $newStmts]; } - return $this->codeStyledPrinter->printToString($nodes); + return $this->codeStyledPrinter->printToString($newStmts, $oldStmts, $oldTokens); } } diff --git a/src/Testing/PHPUnit/AbstractReconstructorTestCase.php b/src/Testing/PHPUnit/AbstractReconstructorTestCase.php index 0826131a9ff..5014fa3e7f6 100644 --- a/src/Testing/PHPUnit/AbstractReconstructorTestCase.php +++ b/src/Testing/PHPUnit/AbstractReconstructorTestCase.php @@ -2,9 +2,9 @@ namespace Rector\Testing\PHPUnit; +use PhpParser\NodeVisitor; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Rector\Contract\Dispatcher\ReconstructorInterface; use Rector\DependencyInjection\ContainerFactory; use Rector\Testing\Application\FileReconstructor; use SplFileInfo; @@ -29,17 +29,8 @@ abstract class AbstractReconstructorTestCase extends TestCase protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void { - $reconstructedFileContent = $this->fileReconstructor->processFileWithReconstructor( - new SplFileInfo($file), $this->getReconstructor() - ); + $reconstructedFileContent = $this->fileReconstructor->processFile(new SplFileInfo($file)); $this->assertStringEqualsFile($reconstructedFile, $reconstructedFileContent); } - - abstract protected function getReconstructorClass(): string; - - private function getReconstructor(): ReconstructorInterface - { - return $this->container->get($this->getReconstructorClass()); - } } diff --git a/src/config/services.yml b/src/config/services.yml index 0446cb18326..526168dd2ed 100644 --- a/src/config/services.yml +++ b/src/config/services.yml @@ -1,3 +1,7 @@ +parameters: + # todo + kernel_class: # for name based service refactoring + services: _defaults: autowire: true @@ -13,5 +17,12 @@ services: $name: "Rector" PhpParser\Parser: factory: ['@Rector\Parser\ParserFactory', 'create'] + PhpParser\Lexer: + factory: ['@Rector\Parser\LexerFactory', 'create'] PhpParser\BuilderFactory: ~ + + # Traverser + PhpParser\NodeTraverser: ~ + # Printer + PhpParser\NodeVisitor\CloningVisitor: ~ PhpParser\PrettyPrinter\Standard: ~ diff --git a/tests/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php b/tests/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php similarity index 52% rename from tests/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php rename to tests/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php index aaa99eb39ef..f0749e4f1ed 100644 --- a/tests/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php +++ b/tests/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorReconstructor/Test.php @@ -1,8 +1,7 @@ property = $property; $this->otherProperty = $otherProperty; } -} \ No newline at end of file +} diff --git a/tests/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor/wrong/wrong.php.inc b/tests/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorReconstructor/wrong/wrong.php.inc similarity index 100% rename from tests/Reconstructor/DependencyInjection/InjectAnnotationToConstructorReconstructor/wrong/wrong.php.inc rename to tests/NodeVisitor/DependencyInjection/InjectAnnotationToConstructorReconstructor/wrong/wrong.php.inc diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/LocalKernel.php b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/LocalKernel.php new file mode 100644 index 00000000000..0f812a061c8 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/LocalKernel.php @@ -0,0 +1,33 @@ +load(__DIR__ . '/services.yml'); + } + + public function getCacheDir(): string + { + return sys_get_temp_dir() . '/_rector_tests_local_kernel_cache'; + } + + public function getLogDir(): string + { + return sys_get_temp_dir() . '/_rector_tests_local_kernel_logs'; + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/services.yml b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/services.yml new file mode 100644 index 00000000000..012c7783afd --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Source/services.yml @@ -0,0 +1,3 @@ +services: + some.class: + class: stdClass \ No newline at end of file diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Test.php b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Test.php new file mode 100644 index 00000000000..612898c60ac --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/Test.php @@ -0,0 +1,26 @@ +doTestFileMatchesExpectedContent( + __DIR__ . '/wrong/wrong.php.inc', + __DIR__ . '/correct/correct.php.inc' + ); + + $this->doTestFileMatchesExpectedContent( + __DIR__ . '/wrong/wrong2.php.inc', + __DIR__ . '/correct/correct2.php.inc' + ); + + $this->doTestFileMatchesExpectedContent( + __DIR__ . '/wrong/wrong3.php.inc', + __DIR__ . '/correct/correct3.php.inc' + ); + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct.php.inc new file mode 100644 index 00000000000..31dadc890f4 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct.php.inc @@ -0,0 +1,17 @@ +stdClass = $stdClass; + } + public function render() + { + $this->stdClass->render(); + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct2.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct2.php.inc new file mode 100644 index 00000000000..341c7fd7df2 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct2.php.inc @@ -0,0 +1,17 @@ +stdClass = $stdClass; + } + public function render() + { + $someService = $this->stdClass; + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct3.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct3.php.inc new file mode 100644 index 00000000000..6f59335fa51 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/correct/correct3.php.inc @@ -0,0 +1,18 @@ +stdClass = $stdClass; + } + public function render() + { + $someService = $this->stdClass; + $someResult = $this->stdClass->callMe(); + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong.php.inc new file mode 100644 index 00000000000..2df871829c6 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong.php.inc @@ -0,0 +1,9 @@ +get('some.class')->render(); + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong2.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong2.php.inc new file mode 100644 index 00000000000..4d9df8cc4bd --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong2.php.inc @@ -0,0 +1,9 @@ +get('some.class'); + } +} diff --git a/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong3.php.inc b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong3.php.inc new file mode 100644 index 00000000000..2dd299abb08 --- /dev/null +++ b/tests/NodeVisitor/DependencyInjection/NamedServicesToConstructorReconstructor/wrong/wrong3.php.inc @@ -0,0 +1,10 @@ +get('some.class'); + $someResult = $this->get('some.class')->callMe(); + } +}