From 48bbc835b14c1f9340ee5d21d522d9e92e98d19d Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Thu, 18 Jun 2020 18:11:17 +0200 Subject: [PATCH] add TokensByFilePathStorage --- .../src/Rector/AbstractFileSystemRector.php | 40 ++++++++++---- phpstan.neon | 1 + .../autodiscovery/src/FileMover/FileMover.php | 28 +++++++--- ...acesToContractNamespaceDirectoryRector.php | 38 +++---------- ...oveEntitiesToEntityDirectoryRectorTest.php | 1 + .../ExpectedRandomInterfaceUseCase.php | 14 +++++ ...ToContractNamespaceDirectoryRectorTest.php | 25 ++++++++- .../Source/RandomInterfaceUseCase.php | 14 +++++ src/Application/FileProcessor.php | 55 ++++++++++++++----- src/Application/RectorApplication.php | 48 +++++++++++----- src/Application/TokensByFilePathStorage.php | 47 ++++++++++++++++ .../AbstractFileSystemRectorTestCase.php | 37 ++++++++++++- 12 files changed, 266 insertions(+), 82 deletions(-) create mode 100644 rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Expected/ExpectedRandomInterfaceUseCase.php create mode 100644 rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Source/RandomInterfaceUseCase.php create mode 100644 src/Application/TokensByFilePathStorage.php diff --git a/packages/file-system-rector/src/Rector/AbstractFileSystemRector.php b/packages/file-system-rector/src/Rector/AbstractFileSystemRector.php index 2f9db549256..322e2a47f39 100644 --- a/packages/file-system-rector/src/Rector/AbstractFileSystemRector.php +++ b/packages/file-system-rector/src/Rector/AbstractFileSystemRector.php @@ -9,8 +9,9 @@ use PhpParser\Lexer; use PhpParser\Node; use PhpParser\ParserFactory; use Rector\Autodiscovery\ValueObject\NodesWithFileDestinationValueObject; +use Rector\Core\Application\FileProcessor; +use Rector\Core\Application\TokensByFilePathStorage; use Rector\Core\Configuration\Configuration; -use Rector\Core\PhpParser\Parser\Parser; use Rector\Core\PhpParser\Printer\BetterStandardPrinter; use Rector\Core\PhpParser\Printer\FormatPerservingPrinter; use Rector\Core\Rector\AbstractRector\AbstractRectorTrait; @@ -41,11 +42,6 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface */ private $oldStmts = []; - /** - * @var Parser - */ - private $parser; - /** * @var Lexer */ @@ -76,11 +72,20 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface */ private $renamedClassesCollector; + /** + * @var TokensByFilePathStorage + */ + private $tokensByFilePathStorage; + + /** + * @var FileProcessor + */ + private $fileProcessor; + /** * @required */ public function autowireAbstractFileSystemRector( - Parser $parser, ParserFactory $parserFactory, Lexer $lexer, FormatPerservingPrinter $formatPerservingPrinter, @@ -89,9 +94,10 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface BetterStandardPrinter $betterStandardPrinter, ParameterProvider $parameterProvider, PostFileProcessor $postFileProcessor, - RenamedClassesCollector $renamedClassesCollector + RenamedClassesCollector $renamedClassesCollector, + TokensByFilePathStorage $tokensByFilePathStorage, + FileProcessor $fileProcessor ): void { - $this->parser = $parser; $this->parserFactory = $parserFactory; $this->lexer = $lexer; $this->formatPerservingPrinter = $formatPerservingPrinter; @@ -101,6 +107,8 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface $this->parameterProvider = $parameterProvider; $this->postFileProcessor = $postFileProcessor; $this->renamedClassesCollector = $renamedClassesCollector; + $this->tokensByFilePathStorage = $tokensByFilePathStorage; + $this->fileProcessor = $fileProcessor; } protected function addClassRename(string $oldClass, string $newClass): void @@ -113,7 +121,12 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface */ protected function parseFileInfoToNodes(SmartFileInfo $smartFileInfo): array { - $oldStmts = $this->parser->parseFileInfo($smartFileInfo); + if (! $this->tokensByFilePathStorage->hasForRealPath($smartFileInfo->getRealPath())) { + $this->fileProcessor->parseFileInfoToLocalCache($smartFileInfo); + } + + [, $oldStmts] = $this->tokensByFilePathStorage->getForRealPath($smartFileInfo->getRealPath()); + // needed for format preserving $this->oldStmts = $oldStmts; @@ -125,7 +138,12 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface */ protected function parseFileInfoToNodesWithoutScope(SmartFileInfo $smartFileInfo): array { - $oldStmts = $this->parser->parseFileInfo($smartFileInfo); + if (! $this->tokensByFilePathStorage->hasForRealPath($smartFileInfo->getRealPath())) { + $this->fileProcessor->parseFileInfoToLocalCache($smartFileInfo); + } + + [, $oldStmts] = $this->tokensByFilePathStorage->getForRealPath($smartFileInfo->getRealPath()); + $this->oldStmts = $oldStmts; return $oldStmts; diff --git a/phpstan.neon b/phpstan.neon index a71e5862b49..37c57ec8a60 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -315,3 +315,4 @@ parameters: - '#Parameter \#1 \$oldMethod of class Rector\\PHPOffice\\ValueObject\\ConditionalSetValue constructor expects string, bool\|int\|string given#' - '#Parameter \#2 \$scope of method PHPStan\\Analyser\\NodeScopeResolver\:\:processNodes\(\) expects PHPStan\\Analyser\\MutatingScope, PHPStan\\Analyser\\Scope given#' - '#Parameter \#1 \$object of function get_class expects object, PhpParser\\Node\|null given#' + - '#Cognitive complexity for "Rector\\Core\\Application\\RectorApplication\:\:runOnFileInfos\(\)" is 10, keep it under 9#' diff --git a/rules/autodiscovery/src/FileMover/FileMover.php b/rules/autodiscovery/src/FileMover/FileMover.php index 0d60ef382ed..36834db3ff2 100644 --- a/rules/autodiscovery/src/FileMover/FileMover.php +++ b/rules/autodiscovery/src/FileMover/FileMover.php @@ -59,13 +59,14 @@ final class FileMover return null; } - $currentClassName = $currentNamespace->name->toString() . '\\' . $smartFileInfo->getBasenameWithoutSuffix(); - // is already in the right group - if (Strings::endsWith((string) $currentNamespace->name, '\\' . $desiredGroupName)) { + $currentNamespaceName = (string) $currentNamespace->name; + if (Strings::endsWith($currentNamespaceName, '\\' . $desiredGroupName)) { return null; } + $currentClassName = $currentNamespaceName . '\\' . $smartFileInfo->getBasenameWithoutSuffix(); + // change namespace to new one $newNamespaceName = $this->createNewNamespaceName($desiredGroupName, $currentNamespace); $newClassName = $this->createNewClassName($smartFileInfo, $newNamespaceName); @@ -76,11 +77,7 @@ final class FileMover } // 1. rename namespace - foreach ($nodes as $node) { - if ($node instanceof Namespace_) { - $node->name = new Name($newNamespaceName); - } - } + $this->renameNamespace($nodes, $newNamespaceName); // 2. return changed nodes and new file destination $newFileDestination = $this->fileRelocationResolver->createNewFileDestination( @@ -110,4 +107,19 @@ final class FileMover { return $newNamespaceName . '\\' . $smartFileInfo->getBasenameWithoutSuffix(); } + + /** + * @param Node[] $nodes + */ + private function renameNamespace(array $nodes, string $newNamespaceName): void + { + foreach ($nodes as $node) { + if (! $node instanceof Namespace_) { + continue; + } + + $node->name = new Name($newNamespaceName); + break; + } + } } diff --git a/rules/autodiscovery/src/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector.php b/rules/autodiscovery/src/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector.php index 0858523acdf..ec83d1cf141 100644 --- a/rules/autodiscovery/src/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector.php +++ b/rules/autodiscovery/src/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector.php @@ -7,11 +7,8 @@ namespace Rector\Autodiscovery\Rector\FileSystem; use PhpParser\Node; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Interface_; -use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; use Rector\Autodiscovery\FileMover\FileMover; -use Rector\Autodiscovery\ValueObject\NodesWithFileDestinationValueObject; -use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\RectorDefinition\CodeSample; use Rector\Core\RectorDefinition\RectorDefinition; use Rector\FileSystemRector\Rector\AbstractFileSystemRector; @@ -82,15 +79,11 @@ PHP { /** @var Interface_|null $interface */ $interface = $this->betterNodeFinder->findFirstInstanceOf($nodes, Interface_::class); + if ($interface === null) { return; } - $oldInterfaceName = $this->getName($interface); - if ($oldInterfaceName === null) { - throw new ShouldNotHappenException(); - } - if ($this->isNetteMagicGeneratedFactory($interface)) { return; } @@ -102,29 +95,14 @@ PHP return; } - $newInterfaceName = $this->resolveNewClassLikeName($nodesWithFileDestination); - $this->removeFile($smartFileInfo); - $this->addClassRename($oldInterfaceName, $newInterfaceName); - $this->printNodesWithFileDestination($nodesWithFileDestination); - } - - private function resolveNewClassLikeName( - NodesWithFileDestinationValueObject $nodesWithFileDestinationValueObject - ): string { - /** @var ClassLike $classLike */ - $classLike = $this->betterNodeFinder->findFirstInstanceOf( - $nodesWithFileDestinationValueObject->getNodes(), - ClassLike::class + $this->addClassRename( + $nodesWithFileDestination->getOldClassName(), + $nodesWithFileDestination->getNewClassName() ); - $classLikeName = $this->getName($classLike); - if ($classLikeName === null) { - throw new ShouldNotHappenException(); - } - - return $classLikeName; + $this->printNodesWithFileDestination($nodesWithFileDestination); } /** @@ -138,11 +116,13 @@ PHP continue; } - if ($returnType->isSuperTypeOf(new ObjectType('Nette\Application\UI\Control'))) { + $className = $returnType->getClassName(); + + if (is_a($className, 'Nette\Application\UI\Control', true)) { return true; } - if ($returnType->isSuperTypeOf(new ObjectType('Nette\Application\UI\Form'))) { + if (is_a($className, 'Nette\Application\UI\Form', true)) { return true; } } diff --git a/rules/autodiscovery/tests/Rector/FileSystem/MoveEntitiesToEntityDirectoryRector/MoveEntitiesToEntityDirectoryRectorTest.php b/rules/autodiscovery/tests/Rector/FileSystem/MoveEntitiesToEntityDirectoryRector/MoveEntitiesToEntityDirectoryRectorTest.php index 5dfab448cc6..49ab35d58ab 100644 --- a/rules/autodiscovery/tests/Rector/FileSystem/MoveEntitiesToEntityDirectoryRector/MoveEntitiesToEntityDirectoryRectorTest.php +++ b/rules/autodiscovery/tests/Rector/FileSystem/MoveEntitiesToEntityDirectoryRector/MoveEntitiesToEntityDirectoryRectorTest.php @@ -15,6 +15,7 @@ final class MoveEntitiesToEntityDirectoryRectorTest extends AbstractFileSystemRe */ public function test(string $originalFile, string $expectedFileLocation, string $expectedFileContent): void { + // @todo add extra files too, wehre the calass name will be changed $this->doTestFile($originalFile); $this->assertFileExists($expectedFileLocation); diff --git a/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Expected/ExpectedRandomInterfaceUseCase.php b/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Expected/ExpectedRandomInterfaceUseCase.php new file mode 100644 index 00000000000..187cb75ed62 --- /dev/null +++ b/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Expected/ExpectedRandomInterfaceUseCase.php @@ -0,0 +1,14 @@ +doTestFile($originalFile); + public function test( + string $originalFile, + string $expectedFileLocation, + string $expectedFileContent, + array $extraFiles = [], + ?string $extraExpectedFileLocation = null, + ?string $expectedExtraFileContent = null + ): void { + $this->doTestFile($originalFile, $extraFiles); $this->assertFileExists($expectedFileLocation); $this->assertFileEquals($expectedFileContent, $expectedFileLocation); + + if ($extraExpectedFileLocation !== null) { + $this->assertFileExists($extraExpectedFileLocation); + + if ($expectedExtraFileContent !== null) { + $this->assertFileEquals($expectedExtraFileContent, $extraExpectedFileLocation); + } + } } public function provideData(): Iterator @@ -27,6 +42,10 @@ final class MoveInterfacesToContractNamespaceDirectoryRectorTest extends Abstrac __DIR__ . '/Source/Entity/RandomInterface.php', $this->getFixtureTempDirectory() . '/Source/Contract/RandomInterface.php', __DIR__ . '/Expected/ExpectedRandomInterface.php', + // extra file + [__DIR__ . '/Source/RandomInterfaceUseCase.php'], + $this->getFixtureTempDirectory() . '/Source/RandomInterfaceUseCase.php', + __DIR__ . '/Expected/ExpectedRandomInterfaceUseCase.php', ]; // skip nette control factory diff --git a/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Source/RandomInterfaceUseCase.php b/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Source/RandomInterfaceUseCase.php new file mode 100644 index 00000000000..158090350ea --- /dev/null +++ b/rules/autodiscovery/tests/Rector/FileSystem/MoveInterfacesToContractNamespaceDirectoryRector/Source/RandomInterfaceUseCase.php @@ -0,0 +1,14 @@ +formatPerservingPrinter = $formatPerservingPrinter; $this->parser = $parser; @@ -89,12 +90,12 @@ final class FileProcessor $this->stubLoader = $stubLoader; $this->affectedFilesCollector = $affectedFilesCollector; $this->postFileProcessor = $postFileProcessor; + $this->tokensByFilePathStorage = $tokensByFilePathStorage; } public function parseFileInfoToLocalCache(SmartFileInfo $smartFileInfo): void { - if (isset($this->tokensByFilePath[$smartFileInfo->getRealPath()])) { - // already parsed + if ($this->tokensByFilePathStorage->hasForRealPath($smartFileInfo->getRealPath())) { return; } @@ -109,12 +110,14 @@ final class FileProcessor } // store tokens by absolute path, so we don't have to print them right now - $this->tokensByFilePath[$smartFileInfo->getRealPath()] = [$newStmts, $oldStmts, $oldTokens]; + $this->tokensByFilePathStorage->addForRealPath($smartFileInfo->getRealPath(), $newStmts, $oldStmts, $oldTokens); } public function printToFile(SmartFileInfo $smartFileInfo): string { - [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePath[$smartFileInfo->getRealPath()]; + [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePathStorage->getForRealPath( + $smartFileInfo->getRealPath() + ); return $this->formatPerservingPrinter->printToFile($smartFileInfo, $newStmts, $oldStmts, $oldTokens); } @@ -125,7 +128,9 @@ final class FileProcessor { $this->makeSureFileIsParsed($smartFileInfo); - [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePath[$smartFileInfo->getRealPath()]; + [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePathStorage->getForRealPath( + $smartFileInfo->getRealPath() + ); return $this->formatPerservingPrinter->printToString($newStmts, $oldStmts, $oldTokens); } @@ -136,7 +141,9 @@ final class FileProcessor $this->makeSureFileIsParsed($smartFileInfo); - [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePath[$smartFileInfo->getRealPath()]; + [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePathStorage->getForRealPath( + $smartFileInfo->getRealPath() + ); $this->currentFileInfoProvider->setCurrentStmt($newStmts); @@ -144,7 +151,7 @@ final class FileProcessor $newStmts = $this->postFileProcessor->traverse($newStmts); // this is needed for new tokens added in "afterTraverse()" - $this->tokensByFilePath[$smartFileInfo->getRealPath()] = [$newStmts, $oldStmts, $oldTokens]; + $this->tokensByFilePathStorage->addForRealPath($smartFileInfo->getRealPath(), $newStmts, $oldStmts, $oldTokens); $this->affectedFilesCollector->removeFromList($smartFileInfo); while ($otherTouchedFile = $this->affectedFilesCollector->getNext()) { @@ -152,6 +159,24 @@ final class FileProcessor } } + public function postFileRefactor(SmartFileInfo $smartFileInfo): void + { + if (! $this->tokensByFilePathStorage->hasForRealPath($smartFileInfo->getRealPath())) { + $this->parseFileInfoToLocalCache($smartFileInfo); + } + + [$newStmts, $oldStmts, $oldTokens] = $this->tokensByFilePathStorage->getForRealPath( + $smartFileInfo->getRealPath() + ); + + $this->currentFileInfoProvider->setCurrentStmt($newStmts); + + $newStmts = $this->postFileProcessor->traverse($newStmts); + + // this is needed for new tokens added in "afterTraverse()" + $this->tokensByFilePathStorage->addForRealPath($smartFileInfo->getRealPath(), $newStmts, $oldStmts, $oldTokens); + } + /** * @return Node[][]|mixed[] */ @@ -161,7 +186,7 @@ final class FileProcessor $oldTokens = $this->lexer->getTokens(); // needed for \Rector\NodeTypeResolver\PHPStan\Scope\NodeScopeResolver - $this->tokensByFilePath[$smartFileInfo->getRealPath()] = [$oldStmts, $oldStmts, $oldTokens]; + $this->tokensByFilePathStorage->addForRealPath($smartFileInfo->getRealPath(), $oldStmts, $oldStmts, $oldTokens); $newStmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($oldStmts, $smartFileInfo); @@ -170,7 +195,7 @@ final class FileProcessor private function makeSureFileIsParsed(SmartFileInfo $smartFileInfo): void { - if (isset($this->tokensByFilePath[$smartFileInfo->getRealPath()])) { + if ($this->tokensByFilePathStorage->hasForRealPath($smartFileInfo->getRealPath())) { return; } diff --git a/src/Application/RectorApplication.php b/src/Application/RectorApplication.php index e08bca306a7..76dba25489c 100644 --- a/src/Application/RectorApplication.php +++ b/src/Application/RectorApplication.php @@ -121,8 +121,8 @@ final class RectorApplication } if (! $this->symfonyStyle->isVerbose() && $this->configuration->showProgressBar()) { - // why 3? one for each cycle, so user sees some activity all the time - $this->symfonyStyle->progressStart($fileCount * 3); + // why 5? one for each cycle, so user sees some activity all the time + $this->symfonyStyle->progressStart($fileCount * 5); $this->configureStepCount($this->symfonyStyle); } @@ -130,6 +130,12 @@ final class RectorApplication // PHPStan has to know about all files! $this->configurePHPStanNodeScopeResolver($fileInfos); + // active only one rule + if ($this->configuration->getOnlyRector() !== null) { + $onlyRector = $this->configuration->getOnlyRector(); + $this->enabledRectorsProvider->addEnabledRector($onlyRector); + } + // 1. parse files to nodes foreach ($fileInfos as $fileInfo) { $this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void { @@ -137,12 +143,6 @@ final class RectorApplication }, 'parsing'); } - // active only one rule - if ($this->configuration->getOnlyRector() !== null) { - $onlyRector = $this->configuration->getOnlyRector(); - $this->enabledRectorsProvider->addEnabledRector($onlyRector); - } - // 2. change nodes with Rectors foreach ($fileInfos as $fileInfo) { $this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void { @@ -150,10 +150,24 @@ final class RectorApplication }, 'refactoring'); } - // 3. print to file or string + // 3. process file system rectors foreach ($fileInfos as $fileInfo) { $this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void { - $this->processFileInfo($smartFileInfo); + $this->processFileSystemRectors($smartFileInfo); + }, 'refactoring with file system'); + } + + // 4. apply post rectors + foreach ($fileInfos as $fileInfo) { + $this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void { + $this->fileProcessor->postFileRefactor($smartFileInfo); + }, 'post rectors'); + } + + // 5. print to file or string + foreach ($fileInfos as $fileInfo) { + $this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void { + $this->printFileInfo($smartFileInfo); }, 'printing'); } @@ -222,7 +236,7 @@ final class RectorApplication } } - private function processFileInfo(SmartFileInfo $fileInfo): void + private function printFileInfo(SmartFileInfo $fileInfo): void { if ($this->removedAndAddedFilesCollector->isFileRemoved($fileInfo)) { // skip, because this file exists no more @@ -235,8 +249,6 @@ final class RectorApplication : $this->fileProcessor->printToFile($fileInfo); $this->errorAndDiffCollector->addFileDiff($fileInfo, $newContent, $oldContent); - - $this->fileSystemFileProcessor->processFileInfo($fileInfo); } private function advance(SmartFileInfo $smartFileInfo, string $phase): void @@ -248,4 +260,14 @@ final class RectorApplication $this->symfonyStyle->progressAdvance(); } } + + private function processFileSystemRectors(SmartFileInfo $smartFileInfo): void + { + if ($this->removedAndAddedFilesCollector->isFileRemoved($smartFileInfo)) { + // skip, because this file exists no more + return; + } + + $this->fileSystemFileProcessor->processFileInfo($smartFileInfo); + } } diff --git a/src/Application/TokensByFilePathStorage.php b/src/Application/TokensByFilePathStorage.php new file mode 100644 index 00000000000..eef71295cb2 --- /dev/null +++ b/src/Application/TokensByFilePathStorage.php @@ -0,0 +1,47 @@ +tokensByFilePath[$realPath] = [$newStmts, $oldStmts, $oldTokens]; + } + + /** + * @todo replace with SmartFileInfo $realPath + */ + public function hasForRealPath(string $realPath): bool + { + return isset($this->tokensByFilePath[$realPath]); + } + + /** + * @todo replace with SmartFileInfo $realPath + * + * @return Node[][]|Stmt[][] + */ + public function getForRealPath(string $realPath): array + { + return $this->tokensByFilePath[$realPath]; + } +} diff --git a/src/Testing/PHPUnit/AbstractFileSystemRectorTestCase.php b/src/Testing/PHPUnit/AbstractFileSystemRectorTestCase.php index fd9d59b4349..c3f0218488d 100644 --- a/src/Testing/PHPUnit/AbstractFileSystemRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractFileSystemRectorTestCase.php @@ -6,6 +6,7 @@ namespace Rector\Core\Testing\PHPUnit; use Nette\Utils\FileSystem; use Nette\Utils\Strings; +use Rector\Core\Application\FileProcessor; use Rector\Core\Application\FileSystem\RemovedAndAddedFilesProcessor; use Rector\Core\Configuration\Configuration; use Rector\Core\HttpKernel\RectorKernel; @@ -27,6 +28,11 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes */ private $removedAndAddedFilesProcessor; + /** + * @var FileProcessor + */ + private $fileProcessor; + protected function setUp(): void { $this->createContainerWithProvidedRector(); @@ -35,6 +41,7 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes $configuration = self::$container->get(Configuration::class); $configuration->setIsDryRun(false); + $this->fileProcessor = self::$container->get(FileProcessor::class); $this->fileSystemFileProcessor = self::$container->get(FileSystemFileProcessor::class); $this->removedAndAddedFilesProcessor = self::$container->get(RemovedAndAddedFilesProcessor::class); } @@ -49,13 +56,36 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes return $temporaryFilePath; } - protected function doTestFile(string $file): string + /** + * @param string[] $extraFiles + */ + protected function doTestFile(string $file, array $extraFiles = []): string { $temporaryFilePath = $this->createTemporaryFilePathFromFilePath($file); - require_once $temporaryFilePath; + $fileInfo = new SmartFileInfo($temporaryFilePath); + $this->fileSystemFileProcessor->processFileInfo($fileInfo); + + $filesInfos = [$fileInfo]; + + foreach ($extraFiles as $extraFile) { + $temporaryExtraFilePath = $this->createTemporaryFilePathFromFilePath($extraFile); + require_once $temporaryExtraFilePath; + $extraFileInfo = new SmartFileInfo($temporaryExtraFilePath); + $this->fileSystemFileProcessor->processFileInfo($extraFileInfo); + + $filesInfos[] = $extraFileInfo; + } + + foreach ($filesInfos as $fileInfo) { + if (! file_exists($fileInfo->getPathname())) { + continue; + } + + $this->fileProcessor->postFileRefactor($fileInfo); + $this->fileProcessor->printToFile($fileInfo); + } - $this->fileSystemFileProcessor->processFileInfo(new SmartFileInfo($temporaryFilePath)); $this->removedAndAddedFilesProcessor->run(); return $temporaryFilePath; @@ -107,6 +137,7 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes $relativeFilePath = $fileInfo->getRelativeFilePathFromDirectory($testCaseDirectory); $temporaryFilePath = $this->getFixtureTempDirectory() . '/' . $relativeFilePath; + FileSystem::delete($temporaryFilePath); FileSystem::copy($file, $temporaryFilePath, true); return $temporaryFilePath;