Refactor AnnotationExtractor fomr FileDiff value object to standalone RectorsChangelogResolver service (#6088)

This commit is contained in:
Tomas Votruba 2021-04-10 18:22:25 +02:00 committed by GitHub
parent 4587a7adcb
commit f82a8dc995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 166 additions and 196 deletions

View File

@ -6,8 +6,8 @@ namespace Rector\Tests\ChangesReporting\Annotation;
use Iterator;
use PHPUnit\Framework\TestCase;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithOutChangelog;
use Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source\RectorWithChangelog;
use Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source\RectorWithOutChangelog;
final class AnnotationExtractorTest extends TestCase
{

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver;
use Rector\ChangesReporting\Annotation\RectorsChangelogResolver;
use Rector\ChangesReporting\ValueObject\RectorWithFileAndLineChange;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\ValueObject\Reporting\FileDiff;
use Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source\RectorWithChangelog;
use Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source\RectorWithOutChangelog;
use Symplify\PackageBuilder\Testing\AbstractKernelTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RectorsChangelogResolverTest extends AbstractKernelTestCase
{
/**
* @var RectorsChangelogResolver
*/
private $rectorsChangelogResolver;
/**
* @var FileDiff
*/
private $fileDiff;
protected function setUp(): void
{
$this->bootKernel(RectorKernel::class);
$this->rectorsChangelogResolver = $this->getService(RectorsChangelogResolver::class);
$this->fileDiff = $this->createFileDiff();
}
public function test(): void
{
$rectorsChangelogs = $this->rectorsChangelogResolver->resolve($this->fileDiff->getRectorClasses());
$expectedRectorsChangelogs = [
RectorWithChangelog::class => 'https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md',
];
$this->assertSame($expectedRectorsChangelogs, $rectorsChangelogs);
}
private function createFileDiff(): FileDiff
{
// This is by intention to test the array_unique functionality
$rectorWithFileAndLineChange1 = new RectorWithFileAndLineChange(
new RectorWithChangelog(),
__DIR__ . '/Source/RectorWithChangelog.php',
1
);
$rectorWithFileAndLineChange2 = new RectorWithFileAndLineChange(
new RectorWithChangelog(),
__DIR__ . '/Source/RectorWithChangelog.php',
1
);
$rectorWithFileAndLineChange3 = new RectorWithFileAndLineChange(
new RectorWithOutChangelog(),
__DIR__ . '/Source/RectorWithOutChangelog.php',
1
);
$rectorWithFileAndLineChanges = [
$rectorWithFileAndLineChange1,
$rectorWithFileAndLineChange2,
$rectorWithFileAndLineChange3,
];
return new FileDiff(new SmartFileInfo(__FILE__), 'foo', 'foo', $rectorWithFileAndLineChanges);
}
}

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\ChangesReporting\ValueObject\Source;
namespace Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source;
use Rector\Core\Contract\Rector\RectorInterface;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\ChangesReporting\ValueObject\Source;
namespace Rector\Tests\ChangesReporting\Annotation\AppliedRectorsChangelogResolver\Source;
use Rector\Core\Contract\Rector\RectorInterface;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

View File

@ -1,74 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\ChangesReporting\ValueObject;
use PHPUnit\Framework\TestCase;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\ChangesReporting\ValueObject\RectorWithFileAndLineChange;
use Rector\Core\ValueObject\Reporting\FileDiff;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithOutChangelog;
use Symplify\SmartFileSystem\SmartFileInfo;
final class FileDiffTest extends TestCase
{
public function testGetRectorClasses(): void
{
$fileDiff = $this->createFileDiff();
$this->assertSame([RectorWithChangelog::class, RectorWithOutChangelog::class], $fileDiff->getRectorClasses());
}
public function testGetRectorClassesWithChangelogUrlAndRectorClassAsKey(): void
{
$fileDiff = $this->createFileDiff();
$this->assertSame(
[
'Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog' => 'https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md',
],
$fileDiff->getRectorClassesWithChangelogUrlAndRectorClassAsKey(new AnnotationExtractor())
);
}
public function testGetRectorClassesWithChangelogUrl(): void
{
$fileDiff = $this->createFileDiff();
$this->assertSame(
[
'Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog (https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md)',
'Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithOutChangelog',
],
$fileDiff->getRectorClassesWithChangelogUrl(new AnnotationExtractor())
);
}
private function createFileDiff(): FileDiff
{
// This is by intention to test the array_unique functionality
$rectorWithFileAndLineChange1 = new RectorWithFileAndLineChange(
new RectorWithChangelog(),
__DIR__ . '/Source/RectorWithChangelog.php',
1
);
$rectorWithFileAndLineChange2 = new RectorWithFileAndLineChange(
new RectorWithChangelog(),
__DIR__ . '/Source/RectorWithChangelog.php',
1
);
$rectorWithFileAndLineChange3 = new RectorWithFileAndLineChange(
new RectorWithOutChangelog(),
__DIR__ . '/Source/RectorWithOutChangelog.php',
1
);
$rectorWithFileAndLineChanges = [
$rectorWithFileAndLineChange1,
$rectorWithFileAndLineChange2,
$rectorWithFileAndLineChange3,
];
return new FileDiff(new SmartFileInfo(__FILE__), 'foo', 'foo', $rectorWithFileAndLineChanges);
}
}

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\ChangesReporting\ValueObject;
use Iterator;
use PHPUnit\Framework\TestCase;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\ChangesReporting\ValueObject\RectorWithFileAndLineChange;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog;
use Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithOutChangelog;
final class RectorWithFileAndLineChangeTest extends TestCase
{
/**
* @dataProvider rectorsWithFileAndLineChange
*/
public function testGetRectorClassWithChangelogUrl(
string $expected,
RectorWithFileAndLineChange $rectorWithFileAndLineChange
): void {
$this->assertSame(
$expected,
$rectorWithFileAndLineChange->getRectorClassWithChangelogUrl(new AnnotationExtractor())
);
}
/**
* @return Iterator<string[]|RectorWithFileAndLineChange[]<class-string<RectorWithOutChangelog>>>
*/
public function rectorsWithFileAndLineChange(): Iterator
{
yield 'Rector with link' => [
'Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithChangelog (https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md)',
new RectorWithFileAndLineChange(new RectorWithChangelog(), __DIR__ . '/Source/RectorWithLink.php', 1),
];
yield 'Rector without link' => [
'Rector\Tests\ChangesReporting\ValueObject\Source\RectorWithOutChangelog',
new RectorWithFileAndLineChange(new RectorWithOutChangelog(), __DIR__ . '/Source/RectorWithLink.php', 1),
];
}
}

View File

@ -1,9 +1,11 @@
<?php
declare(strict_types=1);
namespace Rector\ChangesReporting\Annotation;
use Nette\Utils\Strings;
use Rector\Core\Contract\Rector\RectorInterface;
use ReflectionClass;
/**
@ -12,7 +14,7 @@ use ReflectionClass;
final class AnnotationExtractor
{
/**
* @param class-string<object> $className
* @param class-string<RectorInterface> $className
*/
public function extractAnnotationFromClass(string $className, string $annotation): ?string
{
@ -26,7 +28,6 @@ final class AnnotationExtractor
// @see https://regex101.com/r/oYGaWU/1
$pattern = '#' . preg_quote($annotation, '#') . '\s+(?<content>.*?)$#m';
$matches = Strings::match($docComment, $pattern);
return $matches['content'] ?? null;
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Rector\ChangesReporting\Annotation;
use Rector\Core\Contract\Rector\RectorInterface;
final class RectorsChangelogResolver
{
/**
* @var AnnotationExtractor
*/
private $annotationExtractor;
public function __construct(AnnotationExtractor $annotationExtractor)
{
$this->annotationExtractor = $annotationExtractor;
}
/**
* @param array<class-string<RectorInterface>> $rectorClasses
* @return array<class-string, string>
*/
public function resolve(array $rectorClasses): array
{
$rectorClassesToChangelogUrls = $this->resolveIncludingMissing($rectorClasses);
return array_filter($rectorClassesToChangelogUrls);
}
/**
* @param array<class-string<RectorInterface>> $rectorClasses
* @return array<class-string, string|null>
*/
public function resolveIncludingMissing(array $rectorClasses): array
{
$rectorClassesToChangelogUrls = [];
foreach ($rectorClasses as $rectorClass) {
$changelogUrl = $this->annotationExtractor->extractAnnotationFromClass($rectorClass, '@changelog');
$rectorClassesToChangelogUrls[$rectorClass] = $changelogUrl;
}
return $rectorClassesToChangelogUrls;
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Rector\ChangesReporting\Output;
use Nette\Utils\Strings;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\ChangesReporting\Annotation\RectorsChangelogResolver;
use Rector\ChangesReporting\Application\ErrorAndDiffCollector;
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface;
use Rector\Core\Configuration\Configuration;
@ -46,20 +46,20 @@ final class ConsoleOutputFormatter implements OutputFormatterInterface
private $betterStandardPrinter;
/**
* @var AnnotationExtractor
* @var RectorsChangelogResolver
*/
private $annotationExtractor;
private $rectorsChangelogResolver;
public function __construct(
BetterStandardPrinter $betterStandardPrinter,
Configuration $configuration,
SymfonyStyle $symfonyStyle,
AnnotationExtractor $annotationExtractor
RectorsChangelogResolver $rectorsChangelogResolver
) {
$this->symfonyStyle = $symfonyStyle;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->configuration = $configuration;
$this->annotationExtractor = $annotationExtractor;
$this->rectorsChangelogResolver = $rectorsChangelogResolver;
}
public function report(ErrorAndDiffCollector $errorAndDiffCollector): void
@ -119,10 +119,12 @@ final class ConsoleOutputFormatter implements OutputFormatterInterface
$this->symfonyStyle->writeln($fileDiff->getDiffConsoleFormatted());
$this->symfonyStyle->newLine();
$rectorsChangelogsLines = $this->createRectorChangelogLines($fileDiff);
if ($fileDiff->getRectorChanges() !== []) {
$this->symfonyStyle->writeln('<options=underscore>Applied rules:</>');
$this->symfonyStyle->newLine();
$this->symfonyStyle->listing($fileDiff->getRectorClassesWithChangelogUrl($this->annotationExtractor));
$this->symfonyStyle->listing($rectorsChangelogsLines);
$this->symfonyStyle->newLine();
}
}
@ -232,4 +234,19 @@ final class ConsoleOutputFormatter implements OutputFormatterInterface
$this->configuration->isDryRun() ? 'would have changed (dry-run)' : ($changeCount === 1 ? 'has' : 'have') . ' been changed'
);
}
/**
* @return string[]
*/
private function createRectorChangelogLines(FileDiff $fileDiff): array
{
$rectorsChangelogs = $this->rectorsChangelogResolver->resolveIncludingMissing($fileDiff->getRectorClasses());
$rectorsChangelogsLines = [];
foreach ($rectorsChangelogs as $rectorClass => $changelog) {
$rectorsChangelogsLines[] = $changelog === null ? $rectorClass : $rectorClass . ' ' . $changelog;
}
return $rectorsChangelogsLines;
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Rector\ChangesReporting\Output;
use Nette\Utils\Json;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\ChangesReporting\Annotation\RectorsChangelogResolver;
use Rector\ChangesReporting\Application\ErrorAndDiffCollector;
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface;
use Rector\Core\Configuration\Configuration;
@ -29,18 +29,18 @@ final class JsonOutputFormatter implements OutputFormatterInterface
private $smartFileSystem;
/**
* @var AnnotationExtractor
* @var RectorsChangelogResolver
*/
private $annotationExtractor;
private $rectorsChangelogResolver;
public function __construct(
Configuration $configuration,
SmartFileSystem $smartFileSystem,
AnnotationExtractor $annotationExtractor
RectorsChangelogResolver $rectorsChangelogResolver
) {
$this->configuration = $configuration;
$this->smartFileSystem = $smartFileSystem;
$this->annotationExtractor = $annotationExtractor;
$this->rectorsChangelogResolver = $rectorsChangelogResolver;
}
public function getName(): string
@ -67,9 +67,7 @@ final class JsonOutputFormatter implements OutputFormatterInterface
foreach ($fileDiffs as $fileDiff) {
$relativeFilePath = $fileDiff->getRelativeFilePath();
$appliedRectorsWithChangelog = $fileDiff->getRectorClassesWithChangelogUrlAndRectorClassAsKey(
$this->annotationExtractor
);
$appliedRectorsWithChangelog = $this->rectorsChangelogResolver->resolve($fileDiff->getRectorClasses());
$errorsArray['file_diffs'][] = [
'file' => $relativeFilePath,

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\ChangesReporting\ValueObject;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\Core\Contract\Rector\RectorInterface;
final class RectorWithFileAndLineChange
@ -37,30 +36,14 @@ final class RectorWithFileAndLineChange
return $ruleDefinition->getDescription();
}
/**
* @return class-string<RectorInterface>
*/
public function getRectorClass(): string
{
return get_class($this->rector);
}
public function getRectorClassWithChangelogUrl(AnnotationExtractor $annotationExtractor): string
{
$rectorClass = get_class($this->rector);
$changeLogUrl = $this->getChangelogUrl($annotationExtractor);
if ($changeLogUrl === null) {
return $rectorClass;
}
return sprintf('%s (%s)', $rectorClass, $changeLogUrl);
}
public function getChangelogUrl(AnnotationExtractor $annotationExtractor): ?string
{
$rectorClass = get_class($this->rector);
return $annotationExtractor->extractAnnotationFromClass($rectorClass, '@changelog');
}
public function getLine(): int
{
return $this->line;

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Rector\Core\ValueObject\Reporting;
use Rector\ChangesReporting\Annotation\AnnotationExtractor;
use Rector\ChangesReporting\ValueObject\RectorWithFileAndLineChange;
use Rector\Core\Contract\Rector\RectorInterface;
use Symplify\SmartFileSystem\SmartFileInfo;
final class FileDiff
@ -74,7 +74,7 @@ final class FileDiff
}
/**
* @return string[]
* @return array<class-string<RectorInterface>>
*/
public function getRectorClasses(): array
{
@ -87,45 +87,13 @@ final class FileDiff
}
/**
* @return string[]
*/
public function getRectorClassesWithChangelogUrl(AnnotationExtractor $annotationExtractor): array
{
$rectorClasses = [];
foreach ($this->rectorWithFileAndLineChanges as $rectorWithFileAndLineChange) {
$rectorClasses[] = $rectorWithFileAndLineChange->getRectorClassWithChangelogUrl($annotationExtractor);
}
return $this->sortClasses($rectorClasses);
}
/**
* @return array<string, string>
*/
public function getRectorClassesWithChangelogUrlAndRectorClassAsKey(AnnotationExtractor $annotationExtractor): array
{
$rectorClasses = [];
foreach ($this->rectorWithFileAndLineChanges as $rectorWithFileAndLineChange) {
$changelogUrl = $rectorWithFileAndLineChange->getChangelogUrl($annotationExtractor);
if ($changelogUrl !== null) {
$rectorClasses[$rectorWithFileAndLineChange->getRectorClass()] = $changelogUrl;
}
}
$rectorClasses = array_unique($rectorClasses);
ksort($rectorClasses);
return $rectorClasses;
}
/**
* @param string[] $rectorClasses
* @return string[]
* @template TType as object
* @param array<class-string<TType>> $rectorClasses
* @return array<class-string<TType>>
*/
private function sortClasses(array $rectorClasses): array
{
$rectorClasses = array_unique($rectorClasses);
sort($rectorClasses);
return $rectorClasses;