mirror of https://github.com/rectorphp/rector.git
Merge pull request #315 from rectorphp/annotation-replacer
[PHPUnit] Scenario Annotation replacer Rector
This commit is contained in:
commit
3e8644fec1
32
README.md
32
README.md
|
@ -259,29 +259,53 @@ rectors:
|
|||
rectors:
|
||||
Rector\Rector\Dynamic\ValueObjectRemoverRector:
|
||||
# type: new simple type
|
||||
'ValueObjects\Name': 'string'
|
||||
'ValueObject\Name': 'string'
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```diff
|
||||
- $value = new ValueObjects\Name('Tomas');
|
||||
- $value = new ValueObject\Name('Tomas');
|
||||
+ $value = 'Tomas';
|
||||
```
|
||||
|
||||
```diff
|
||||
/**
|
||||
-* @var ValueObjects\Name
|
||||
-* @var ValueObject\Name
|
||||
+* @var string
|
||||
*/
|
||||
private $name;
|
||||
```
|
||||
|
||||
```diff
|
||||
- public function someMethod(ValueObjects\Name $name) { ...
|
||||
- public function someMethod(ValueObject\Name $name) { ...
|
||||
+ public function someMethod(string $name) { ...
|
||||
```
|
||||
|
||||
## Replace Property and Method Annotations
|
||||
|
||||
```yml
|
||||
rectors:
|
||||
Rector\Rector\Dynamic\AnnotationReplacerRector:
|
||||
# type
|
||||
PHPUnit\Framework\TestCase:
|
||||
# old annotation: new annotation
|
||||
scenario: test
|
||||
```
|
||||
|
||||
```diff
|
||||
final class SomeTest extends PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
- * @scenario
|
||||
+ * @test
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Turn Magic to Methods
|
||||
|
||||
### Replace `get/set` magic methods with real ones
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rector\ReflectionDocBlock\NodeAnalyzer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||
|
@ -60,6 +61,19 @@ final class DocBlockAnalyzer
|
|||
$this->saveNewDocBlockToNode($node, $docBlock);
|
||||
}
|
||||
|
||||
public function replaceAnnotationInNode(Node $node, string $oldAnnotation, string $newAnnotation): void
|
||||
{
|
||||
if (! $node->getDocComment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldContent = $node->getDocComment()->getText();
|
||||
$newContent = Strings::replace($oldContent, sprintf('#@%s#', $oldAnnotation), '@' . $newAnnotation);
|
||||
|
||||
$doc = new Doc($newContent);
|
||||
$node->setDocComment($doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]|null
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Rector\Dynamic;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Node\Attribute;
|
||||
use Rector\Rector\AbstractPHPUnitRector;
|
||||
use Rector\ReflectionDocBlock\NodeAnalyzer\DocBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* Before:
|
||||
* - @scenario
|
||||
*
|
||||
* After:
|
||||
* - @test
|
||||
*/
|
||||
final class AnnotationReplacerRector extends AbstractPHPUnitRector
|
||||
{
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private $classToAnnotationMap = [];
|
||||
|
||||
/**
|
||||
* @var DocBlockAnalyzer
|
||||
*/
|
||||
private $docBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $activeAnnotationMap = [];
|
||||
|
||||
/**
|
||||
* @param string[][] $classToAnnotationMap
|
||||
*/
|
||||
public function __construct(array $classToAnnotationMap, DocBlockAnalyzer $docBlockAnalyzer)
|
||||
{
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->classToAnnotationMap = $classToAnnotationMap;
|
||||
}
|
||||
|
||||
public function isCandidate(Node $node): bool
|
||||
{
|
||||
if ($this->shouldSkip($node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
foreach ($this->classToAnnotationMap as $type => $annotationMap) {
|
||||
if (! in_array($type, $parentNode->getAttribute(Attribute::TYPES), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->activeAnnotationMap = $annotationMap;
|
||||
|
||||
if ($this->hasAnyAnnotation($node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
foreach ($this->activeAnnotationMap as $oldAnnotation => $newAnnotation) {
|
||||
$this->docBlockAnalyzer->replaceAnnotationInNode($node, $oldAnnotation, $newAnnotation);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function shouldSkip(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof ClassMethod && ! $node instanceof Property) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Node|null $parentNode */
|
||||
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
if (! $parentNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasAnyAnnotation(Node $node): bool
|
||||
{
|
||||
foreach ($this->activeAnnotationMap as $oldAnnotation => $newAnnotation) {
|
||||
if ($this->docBlockAnalyzer->hasAnnotation($node, $oldAnnotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
rectors:
|
||||
Rector\Rector\Dynamic\AnnotationReplacerRector:
|
||||
PHPUnit\Framework\TestCase:
|
||||
scenario: test
|
|
@ -0,0 +1,11 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
final class MyTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Dynamic\AnnotationReplacerRector;
|
||||
|
||||
use Rector\Rector\Dynamic\AnnotationReplacerRector;
|
||||
use Rector\Testing\PHPUnit\AbstractConfigurableRectorTestCase;
|
||||
|
||||
final class ScenarioToTestAnnotationRectorTest extends AbstractConfigurableRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideWrongToFixedFiles()
|
||||
*/
|
||||
public function test(string $wrong, string $fixed): void
|
||||
{
|
||||
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public function provideWrongToFixedFiles(): array
|
||||
{
|
||||
return [
|
||||
[__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getRectorClasses(): array
|
||||
{
|
||||
return [AnnotationReplacerRector::class];
|
||||
}
|
||||
|
||||
protected function provideConfig(): string
|
||||
{
|
||||
return __DIR__ . '/config/rector.yml';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
final class MyTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @scenario
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
rectors:
|
||||
Rector\Rector\Dynamic\AnnotationReplacerRector:
|
||||
PHPUnit\Framework\TestCase:
|
||||
scenario: test
|
Loading…
Reference in New Issue