[SOLID] Add FinalizeClassesWithoutChildrenRector

This commit is contained in:
Tomas Votruba 2019-04-07 00:18:23 +02:00
parent 14c3251887
commit c1d0bd1556
8 changed files with 186 additions and 3 deletions

View File

@ -61,7 +61,8 @@
"Rector\\PhpSpecToPHPUnit\\": "packages/PhpSpecToPHPUnit/src",
"Rector\\Shopware\\": "packages/Shopware/src",
"Rector\\NetteTesterToPHPUnit\\": "packages/NetteTesterToPHPUnit/src",
"Rector\\Nette\\": "packages/Nette/src"
"Rector\\Nette\\": "packages/Nette/src",
"Rector\\SOLID\\": "packages/SOLID/src"
}
},
"autoload-dev": {
@ -94,7 +95,8 @@
"Rector\\PHPStanExtensions\\": "utils/PHPStanExtensions/src",
"Rector\\Shopware\\Tests\\": "packages/Shopware/tests",
"Rector\\NetteTesterToPHPUnit\\Tests\\": "packages/NetteTesterToPHPUnit/tests",
"Rector\\Nette\\Tests\\": "packages/Nette/tests"
"Rector\\Nette\\Tests\\": "packages/Nette/tests",
"Rector\\SOLID\\Tests\\": "packages/SOLID/tests"
},
"classmap": [
"packages/Symfony/tests/Rector/FrameworkBundle/AbstractToConstructorInjectionRectorSource",
@ -138,4 +140,4 @@
"dev-master": "0.5-dev"
}
}
}
}

View File

@ -0,0 +1,2 @@
services:
Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: ~

View File

@ -200,4 +200,9 @@ final class ClassLikeNodeCollector
return $classNodes;
}
public function hasClassChildren(string $class): bool
{
return $this->findChildrenOfClass($class) !== [];
}
}

View File

@ -0,0 +1,99 @@
<?php declare(strict_types=1);
namespace Rector\SOLID\Rector\Class_;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\NodeTypeResolver\Application\ClassLikeNodeCollector;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class FinalizeClassesWithoutChildrenRector extends AbstractRector
{
/**
* @var ClassLikeNodeCollector
*/
private $classLikeNodeCollector;
public function __construct(ClassLikeNodeCollector $classLikeNodeCollector)
{
$this->classLikeNodeCollector = $classLikeNodeCollector;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Finalize every class that has no children', [
new CodeSample(
<<<'CODE_SAMPLE'
class FirstClass
{
}
class SecondClass
{
}
class ThirdClass extends SecondClass
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class FirstClass
{
}
class SecondClass
{
}
final class ThirdClass extends SecondClass
{
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Node\Stmt\Class_::class];
}
/**
* @param Node\Stmt\Class_ $node
*/
public function refactor(Node $node): ?Node
{
if ($node->isFinal() || $node->isAbstract() || $node->isAnonymous()) {
return null;
}
if ($this->isDoctrineEntity($node)) {
return null;
}
/** @var string $class */
$class = $this->getName($node);
if ($this->classLikeNodeCollector->hasClassChildren($class)) {
return null;
}
$node->flags |= Node\Stmt\Class_::MODIFIER_FINAL;
return $node;
}
private function isDoctrineEntity(Node $node): bool
{
if ($node->getDocComment() === null) {
return false;
}
return Strings::contains($node->getDocComment()->getText(), 'Entity');
}
}

View File

@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace Rector\SOLID\Tests\Rector\Class_\FinalizeClassesWithoutChildrenRector;
use Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class FinalizeClassesWithoutChildrenRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
__DIR__ . '/Fixture/fixture.php.inc',
__DIR__ . '/Fixture/skip.php.inc',
__DIR__ . '/Fixture/entity.php.inc',
]);
}
protected function getRectorClass(): string
{
return FinalizeClassesWithoutChildrenRector::class;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Rector\SOLID\Tests\Rector\Class_\FinalizeClassesWithoutChildrenRector\Fixture;
/**
* @ORM\Entity
*/
class Product
{
}

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\SOLID\Tests\Rector\Class_\FinalizeClassesWithoutChildrenRector\Fixture;
class FirstClass
{
}
class SecondClass
{
}
class ThirdClass extends SecondClass
{
}
?>
-----
<?php
namespace Rector\SOLID\Tests\Rector\Class_\FinalizeClassesWithoutChildrenRector\Fixture;
final class FirstClass
{
}
class SecondClass
{
}
final class ThirdClass extends SecondClass
{
}
?>

View File

@ -0,0 +1,7 @@
<?php
namespace Rector\SOLID\Tests\Rector\Class_\FinalizeClassesWithoutChildrenRector\Fixture;
abstract class SkipAbstractClass
{
}