Merge pull request #3565 from rectorphp/rename-neon-yaml

Add post class move rename in neon/yaml files
This commit is contained in:
kodiakhq[bot] 2020-06-21 17:07:46 +00:00 committed by GitHub
commit aabe7694a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 269 additions and 125 deletions

View File

@ -0,0 +1,2 @@
services:
Rector\Autodiscovery\Tests\Rector\FileSystem\MoveServicesBySuffixToDirectoryRector\SourceMutualRename\Mapper\Nested\AbstractBaseMapper: null

View File

@ -60,6 +60,12 @@ final class MutualRenameTest extends AbstractFileSystemRectorTestCase
'location' => $this->getFixtureTempDirectory() . '/SourceMutualRename/Mapper/Nested/AbstractBaseMapper.php',
'content' => __DIR__ . '/ExpectedMutualRename/Mapper/Nested/AbstractBaseMapper.php.inc',
],
// includes NEON/YAML file renames
__DIR__ . '/SourceMutualRename/config/some_config.neon' => [
'location' => $this->getFixtureTempDirectory() . '/SourceMutualRename/config/some_config.neon',
'content' => __DIR__ . '/ExpectedMutualRename/config/expected_some_config.neon',
],
],
];
}

View File

@ -0,0 +1,2 @@
services:
Rector\Autodiscovery\Tests\Rector\FileSystem\MoveServicesBySuffixToDirectoryRector\SourceMutualRename\Controller\Nested\AbstractBaseMapper: null

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Rector\PSR4\Collector;
use Rector\Core\Configuration\ChangeConfiguration;
final class RenamedClassesCollector
{
/**
@ -11,6 +13,16 @@ final class RenamedClassesCollector
*/
private $oldToNewClass = [];
/**
* @var ChangeConfiguration
*/
private $changeConfiguration;
public function __construct(ChangeConfiguration $changeConfiguration)
{
$this->changeConfiguration = $changeConfiguration;
}
public function addClassRename(string $oldClass, string $newClass): void
{
$this->oldToNewClass[$oldClass] = $newClass;
@ -21,6 +33,6 @@ final class RenamedClassesCollector
*/
public function getOldToNewClasses(): array
{
return $this->oldToNewClass;
return array_merge($this->oldToNewClass, $this->changeConfiguration->getOldToNewClasses());
}
}

View File

@ -43,7 +43,9 @@ final class RenameClassRector extends AbstractRector
$this->oldToNewClasses = $oldToNewClasses;
$this->classRenamer = $classRenamer;
$changeConfiguration->setOldToNewClasses($oldToNewClasses);
if ($oldToNewClasses !== []) {
$changeConfiguration->setOldToNewClasses($oldToNewClasses);
}
}
public function getDefinition(): RectorDefinition

View File

@ -0,0 +1,7 @@
services:
-
class: Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\OldClass
-----
services:
-
class: Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\NewClass

View File

@ -0,0 +1,5 @@
services:
Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\OldClass: null
-----
services:
Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\NewClass: null

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Rector\Renaming\Tests\Rector\Class_\RenameClassRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Renaming\Rector\Class_\RenameClassRector;
use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\NewClass;
use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\OldClass;
final class RenameNeonYamlRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureRenameNeonYaml', '#\.(neon|yaml)$#');
}
/**
* @return mixed[]
*/
protected function getRectorsWithConfiguration(): array
{
return [
RenameClassRector::class => [
'$oldToNewClasses' => [
OldClass::class => NewClass::class,
],
],
];
}
}

View File

@ -111,11 +111,11 @@ final class RectorApplication
}
/**
* @param SmartFileInfo[] $fileInfos
* @param SmartFileInfo[] $phpFileInfos
*/
public function runOnFileInfos(array $fileInfos): void
public function runOnFileInfos(array $phpFileInfos): void
{
$fileCount = count($fileInfos);
$fileCount = count($phpFileInfos);
if ($fileCount === 0) {
return;
}
@ -128,7 +128,7 @@ final class RectorApplication
}
// PHPStan has to know about all files!
$this->configurePHPStanNodeScopeResolver($fileInfos);
$this->configurePHPStanNodeScopeResolver($phpFileInfos);
// active only one rule
if ($this->configuration->getOnlyRector() !== null) {
@ -137,36 +137,36 @@ final class RectorApplication
}
// 1. parse files to nodes
foreach ($fileInfos as $fileInfo) {
$this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void {
foreach ($phpFileInfos as $phpFileInfo) {
$this->tryCatchWrapper($phpFileInfo, function (SmartFileInfo $smartFileInfo): void {
$this->fileProcessor->parseFileInfoToLocalCache($smartFileInfo);
}, 'parsing');
}
// 2. change nodes with Rectors
foreach ($fileInfos as $fileInfo) {
$this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void {
foreach ($phpFileInfos as $phpFileInfo) {
$this->tryCatchWrapper($phpFileInfo, function (SmartFileInfo $smartFileInfo): void {
$this->fileProcessor->refactor($smartFileInfo);
}, 'refactoring');
}
// 3. process file system rectors
foreach ($fileInfos as $fileInfo) {
$this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void {
foreach ($phpFileInfos as $phpFileInfo) {
$this->tryCatchWrapper($phpFileInfo, function (SmartFileInfo $smartFileInfo): void {
$this->processFileSystemRectors($smartFileInfo);
}, 'refactoring with file system');
}
// 4. apply post rectors
foreach ($fileInfos as $fileInfo) {
$this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void {
foreach ($phpFileInfos as $phpFileInfo) {
$this->tryCatchWrapper($phpFileInfo, 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 {
foreach ($phpFileInfos as $phpFileInfo) {
$this->tryCatchWrapper($phpFileInfo, function (SmartFileInfo $smartFileInfo): void {
$this->printFileInfo($smartFileInfo);
}, 'printing');
}

View File

@ -16,9 +16,9 @@ use Rector\Core\Console\Output\OutputFormatterCollector;
use Rector\Core\EventDispatcher\Event\AfterReportEvent;
use Rector\Core\FileSystem\FilesFinder;
use Rector\Core\Guard\RectorGuard;
use Rector\Core\NeonYaml\NeonYamlProcessor;
use Rector\Core\PhpParser\NodeTraverser\RectorNodeTraverser;
use Rector\Core\Stubs\StubLoader;
use Rector\Core\Yaml\YamlProcessor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -77,9 +77,9 @@ final class ProcessCommand extends AbstractCommand
private $stubLoader;
/**
* @var YamlProcessor
* @var NeonYamlProcessor
*/
private $yamlProcessor;
private $neonYamlProcessor;
/**
* @var UnchangedFilesFilter
@ -111,7 +111,7 @@ final class ProcessCommand extends AbstractCommand
OutputFormatterCollector $outputFormatterCollector,
RectorNodeTraverser $rectorNodeTraverser,
StubLoader $stubLoader,
YamlProcessor $yamlProcessor,
NeonYamlProcessor $neonYamlProcessor,
ChangedFilesDetector $changedFilesDetector,
UnchangedFilesFilter $unchangedFilesFilter,
SymfonyStyle $symfonyStyle,
@ -126,7 +126,7 @@ final class ProcessCommand extends AbstractCommand
$this->outputFormatterCollector = $outputFormatterCollector;
$this->rectorNodeTraverser = $rectorNodeTraverser;
$this->stubLoader = $stubLoader;
$this->yamlProcessor = $yamlProcessor;
$this->neonYamlProcessor = $neonYamlProcessor;
$this->unchangedFilesFilter = $unchangedFilesFilter;
parent::__construct();
@ -229,11 +229,13 @@ final class ProcessCommand extends AbstractCommand
$this->symfonyStyle->listing($phpFileInfos);
}
$this->yamlProcessor->run($source);
$this->configuration->setFileInfos($phpFileInfos);
$this->rectorApplication->runOnFileInfos($phpFileInfos);
// must run after PHP rectors, because they might change class names, and these class names must be changed in configs
$neonYamlFileInfos = $this->filesFinder->findInDirectoriesAndFiles($source, ['neon', 'yaml']);
$this->neonYamlProcessor->runOnFileInfos($neonYamlFileInfos);
$this->reportZeroCacheRectorsCondition();
// report diffs and errors

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Rector\Core\Console\Command;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\NeonYaml\YamlPrinter;
use Rector\Core\Php\TypeAnalyzer;
use Rector\Core\Yaml\YamlPrinter;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use ReflectionClass;

View File

@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace Rector\Core\NeonYaml;
use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use Rector\Core\Configuration\ChangeConfiguration;
use Rector\Core\Configuration\Configuration;
use Rector\PSR4\Collector\RenamedClassesCollector;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\SmartFileSystem\SmartFileInfo;
final class NeonYamlProcessor
{
/**
* @var Configuration
*/
private $configuration;
/**
* @var ChangeConfiguration
*/
private $changeConfiguration;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var RenamedClassesCollector
*/
private $renamedClassesCollector;
public function __construct(
Configuration $configuration,
ChangeConfiguration $changeConfiguration,
SymfonyStyle $symfonyStyle,
RenamedClassesCollector $renamedClassesCollector
) {
$this->configuration = $configuration;
$this->changeConfiguration = $changeConfiguration;
$this->symfonyStyle = $symfonyStyle;
$this->renamedClassesCollector = $renamedClassesCollector;
}
/**
* @param SmartFileInfo[] $neonYamlFileInfos
*/
public function runOnFileInfos(array $neonYamlFileInfos): void
{
foreach ($neonYamlFileInfos as $neonYamlFileInfo) {
$this->processFileInfo($neonYamlFileInfo);
}
}
public function processFileInfo(SmartFileInfo $neonYamlFileInfo): string
{
$oldContent = $neonYamlFileInfo->getContents();
$newContent = $this->renameClasses($oldContent);
// nothing has changed
if ($oldContent === $newContent) {
return $oldContent;
}
$this->reportFileContentChange($neonYamlFileInfo, $newContent);
return $newContent;
}
private function renameClasses(string $newContent): string
{
foreach ($this->getOldToNewClasses() as $oldClass => $newClass) {
/** @var string $newContent */
$newContent = Strings::replace($newContent, '#' . preg_quote($oldClass, '#') . '#', $newClass);
}
return $newContent;
}
/**
* @return string[]
*/
private function getOldToNewClasses(): array
{
return array_merge(
$this->changeConfiguration->getOldToNewClasses(),
$this->renamedClassesCollector->getOldToNewClasses()
);
}
private function reportFileContentChange(SmartFileInfo $neonYamlFileInfo, string $newContent): void
{
$relativeFilePath = $neonYamlFileInfo->getRelativeFilePathFromCwd();
if ($this->configuration->isDryRun()) {
$this->symfonyStyle->note(
sprintf('File "%s" would be changed ("dry-run" is on now)', $relativeFilePath)
);
} else {
$this->symfonyStyle->note(sprintf('File "%s" was changed', $relativeFilePath));
FileSystem::write($neonYamlFileInfo->getRealPath(), $newContent, $neonYamlFileInfo->getPerms());
}
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Core\Yaml;
namespace Rector\Core\NeonYaml;
use Symfony\Component\Yaml\Yaml;

View File

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Rector\Core\Testing\Application;
use Rector\Core\Configuration\ChangeConfiguration;
use Rector\Core\Testing\PHPUnit\StaticPHPUnitEnvironment;
use Rector\Renaming\Rector\Class_\RenameClassRector;
final class EnabledRectorsProvider
{
/**
@ -11,12 +15,27 @@ final class EnabledRectorsProvider
*/
private $enabledRectors = [];
/**
* @var ChangeConfiguration
*/
private $changeConfiguration;
public function __construct(ChangeConfiguration $changeConfiguration)
{
$this->changeConfiguration = $changeConfiguration;
}
/**
* @param mixed[] $configuration
*/
public function addEnabledRector(string $rector, array $configuration = []): void
{
$this->enabledRectors[$rector] = $configuration;
if (StaticPHPUnitEnvironment::isPHPUnitRun() && is_a($rector, RenameClassRector::class, true)) {
// only in unit tests
$this->changeConfiguration->setOldToNewClasses($configuration['$oldToNewClasses'] ?? []);
}
}
public function reset(): void

View File

@ -10,6 +10,7 @@ use Rector\Core\Application\FileProcessor;
use Rector\Core\Application\FileSystem\RemovedAndAddedFilesProcessor;
use Rector\Core\Configuration\Configuration;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\NeonYaml\NeonYamlProcessor;
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
use Rector\FileSystemRector\FileSystemFileProcessor;
use ReflectionClass;
@ -33,6 +34,11 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes
*/
private $fileProcessor;
/**
* @var NeonYamlProcessor
*/
private $neonYamlProcessor;
protected function setUp(): void
{
$this->createContainerWithProvidedRector();
@ -44,6 +50,7 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes
$this->fileProcessor = self::$container->get(FileProcessor::class);
$this->fileSystemFileProcessor = self::$container->get(FileSystemFileProcessor::class);
$this->removedAndAddedFilesProcessor = self::$container->get(RemovedAndAddedFilesProcessor::class);
$this->neonYamlProcessor = self::$container->get(NeonYamlProcessor::class);
}
/**
@ -85,8 +92,12 @@ abstract class AbstractFileSystemRectorTestCase extends AbstractGenericRectorTes
continue;
}
$this->fileProcessor->postFileRefactor($fileInfo);
$this->fileProcessor->printToFile($fileInfo);
if (in_array($fileInfo->getSuffix(), ['neon', 'yaml'], true)) {
$this->neonYamlProcessor->processFileInfo($fileInfo);
} else {
$this->fileProcessor->postFileRefactor($fileInfo);
$this->fileProcessor->printToFile($fileInfo);
}
}
$this->removedAndAddedFilesProcessor->run();

View File

@ -73,9 +73,9 @@ abstract class AbstractGenericRectorTestCase extends AbstractKernelTestCase
*/
abstract protected function getRectorInterface(): string;
protected function yieldFilesFromDirectory(string $directory): Iterator
protected function yieldFilesFromDirectory(string $directory, string $suffix = '*.php.inc'): Iterator
{
return StaticFixtureProvider::yieldFilesFromDirectory($directory, '*.php.inc');
return StaticFixtureProvider::yieldFilesFromDirectory($directory, $suffix);
}
protected function setParameter(string $name, $value): void

View File

@ -16,6 +16,7 @@ use Rector\Core\Configuration\Option;
use Rector\Core\Contract\Rector\PhpRectorInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\NeonYaml\NeonYamlProcessor;
use Rector\Core\Set\Set;
use Rector\Core\Stubs\StubLoader;
use Rector\Core\Testing\Application\EnabledRectorsProvider;
@ -71,6 +72,11 @@ abstract class AbstractRectorTestCase extends AbstractGenericRectorTestCase
*/
private $runnableRectorFactory;
/**
* @var NeonYamlProcessor
*/
private $neonYamlProcessor;
protected function setUp(): void
{
parent::setUp();
@ -107,6 +113,7 @@ abstract class AbstractRectorTestCase extends AbstractGenericRectorTestCase
$symfonyStyle->setVerbosity(OutputInterface::VERBOSITY_QUIET);
$this->fileProcessor = static::$container->get(FileProcessor::class);
$this->neonYamlProcessor = static::$container->get(NeonYamlProcessor::class);
$this->parameterProvider = static::$container->get(ParameterProvider::class);
// needed for PHPStan, because the analyzed file is just create in /temp
@ -271,21 +278,27 @@ abstract class AbstractRectorTestCase extends AbstractGenericRectorTestCase
): void {
$this->setParameter(Option::SOURCE, [$originalFileInfo->getRealPath()]);
// life-cycle trio :)
$this->fileProcessor->parseFileInfoToLocalCache($originalFileInfo);
$this->fileProcessor->refactor($originalFileInfo);
if ($originalFileInfo->getSuffix() === 'php') {
// life-cycle trio :)
$this->fileProcessor->parseFileInfoToLocalCache($originalFileInfo);
$this->fileProcessor->refactor($originalFileInfo);
$this->fileProcessor->postFileRefactor($originalFileInfo);
$this->fileProcessor->postFileRefactor($originalFileInfo);
// mimic post-rectors
// mimic post-rectors
$changedContent = $this->fileProcessor->printToString($originalFileInfo);
$changedContent = $this->fileProcessor->printToString($originalFileInfo);
$removedAndAddedFilesProcessor = self::$container->get(RemovedAndAddedFilesProcessor::class);
$removedAndAddedFilesProcessor->run();
} elseif (in_array($originalFileInfo->getSuffix(), ['neon', 'yaml'], true)) {
$changedContent = $this->neonYamlProcessor->processFileInfo($originalFileInfo);
} else {
$message = sprintf('Suffix "%s" is not supported yet', $originalFileInfo->getSuffix());
throw new ShouldNotHappenException($message);
}
$causedByFixtureMessage = $fixtureFileInfo->getRelativeFilePathFromCwd();
$removedAndAddedFilesProcessor = self::$container->get(RemovedAndAddedFilesProcessor::class);
$removedAndAddedFilesProcessor->run();
try {
$this->assertStringEqualsFile($expectedFileInfo->getRealPath(), $changedContent, $causedByFixtureMessage);
} catch (ExpectationFailedException $expectationFailedException) {

View File

@ -37,7 +37,7 @@ final class FixtureSplitter
FileSystem::write($expectedFile, $expectedContent);
// file needs to be autoload so PHPStan can analyze
if ($autoloadTestFixture) {
if ($autoloadTestFixture && Strings::match($smartFileInfo->getFilename(), '#\.php(\.inc)?$#')) {
require_once $originalFile;
}

View File

@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Yaml;
use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use Rector\Core\Configuration\ChangeConfiguration;
use Rector\Core\Configuration\Configuration;
use Rector\Core\FileSystem\FilesFinder;
use Symfony\Component\Console\Style\SymfonyStyle;
final class YamlProcessor
{
/**
* @var Configuration
*/
private $configuration;
/**
* @var FilesFinder
*/
private $filesFinder;
/**
* @var ChangeConfiguration
*/
private $changeConfiguration;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
public function __construct(
Configuration $configuration,
FilesFinder $filesFinder,
ChangeConfiguration $changeConfiguration,
SymfonyStyle $symfonyStyle
) {
$this->configuration = $configuration;
$this->filesFinder = $filesFinder;
$this->changeConfiguration = $changeConfiguration;
$this->symfonyStyle = $symfonyStyle;
}
/**
* @param string[] $source
*/
public function run(array $source): void
{
$yamlFileInfos = $this->filesFinder->findInDirectoriesAndFiles($source, ['yaml']);
// 1. raw class rename
$oldToNewClasses = $this->changeConfiguration->getOldToNewClasses();
if ($oldToNewClasses === []) {
return;
}
foreach ($yamlFileInfos as $yamlFileInfo) {
$oldContent = $yamlFileInfo->getContents();
$newContent = $oldContent;
foreach ($oldToNewClasses as $oldClass => $newClass) {
/** @var string $newContent */
$newContent = Strings::replace($newContent, '#' . preg_quote($oldClass, '#') . '#', $newClass);
}
// nothing has changed
if ($oldContent === $newContent) {
continue;
}
$relativeFilePath = $yamlFileInfo->getRelativeFilePathFromCwd();
if ($this->configuration->isDryRun()) {
$this->symfonyStyle->note(sprintf('File %s would be changed (dry-run is on now)', $relativeFilePath));
continue;
}
$this->symfonyStyle->note(sprintf('File %s was changed', $relativeFilePath));
FileSystem::write($yamlFileInfo->getRealPath(), $newContent);
}
}
}