mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-19 14:27:14 +01:00
[Restoratoin] Add RemoveUselessJustForSakeInterfaceRector
This commit is contained in:
parent
690e2e9546
commit
0774576a86
@ -1718,7 +1718,7 @@ catch (CatchedType $catchedVariable) {
|
||||
#### Public Properties
|
||||
|
||||
* `$types` - `/** @var Node\Name[] Types of exceptions to catch */`
|
||||
* `$var` - `/** @var Expr\Variable Variable for exception */`
|
||||
* `$var` - `/** @var Expr\Variable|null Variable for exception */`
|
||||
* `$stmts` - `/** @var Node\Stmt[] Statements */`
|
||||
<br>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# All 506 Rectors Overview
|
||||
# All 507 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
@ -56,7 +56,7 @@
|
||||
- [Refactoring](#refactoring) (2)
|
||||
- [RemovingStatic](#removingstatic) (4)
|
||||
- [Renaming](#renaming) (10)
|
||||
- [Restoration](#restoration) (5)
|
||||
- [Restoration](#restoration) (6)
|
||||
- [SOLID](#solid) (12)
|
||||
- [Sensio](#sensio) (3)
|
||||
- [StrictCodeQuality](#strictcodequality) (1)
|
||||
@ -9319,6 +9319,33 @@ Remove final from Doctrine entities
|
||||
|
||||
<br>
|
||||
|
||||
### `RemoveUselessJustForSakeInterfaceRector`
|
||||
|
||||
- class: [`Rector\Restoration\Rector\Class_\RemoveUselessJustForSakeInterfaceRector`](/../master/rules/restoration/src/Rector/Class_/RemoveUselessJustForSakeInterfaceRector.php)
|
||||
|
||||
Remove interface, that are added just for its sake, but nowhere useful
|
||||
|
||||
```diff
|
||||
-class SomeClass implements OnlyHereUsedInterface
|
||||
+class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
-interface OnlyHereUsedInterface
|
||||
-{
|
||||
-}
|
||||
-
|
||||
class SomePresenter
|
||||
{
|
||||
- public function __construct(OnlyHereUsedInterface $onlyHereUsed)
|
||||
+ public function __construct(SomeClass $onlyHereUsed)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## SOLID
|
||||
|
||||
### `AddFalseDefaultToBoolPropertyRector`
|
||||
|
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Rector\Class_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\Testing\PHPUnit\StaticPHPUnitEnvironment;
|
||||
use Rector\NodeCollector\NodeFinder\ClassLikeParsedNodesFinder;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use ReflectionClass;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://amateri.com for sponsoring this rule
|
||||
*
|
||||
* @see \Rector\Restoration\Tests\Rector\Class_\RemoveUselessJustForSakeInterfaceRector\RemoveUselessJustForSakeInterfaceRectorTest
|
||||
*/
|
||||
final class RemoveUselessJustForSakeInterfaceRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $interfacePattern;
|
||||
|
||||
/**
|
||||
* @var RenamedClassesCollector
|
||||
*/
|
||||
private $renamedClassesCollector;
|
||||
|
||||
public function __construct(
|
||||
RenamedClassesCollector $renamedClassesCollector,
|
||||
ClassLikeParsedNodesFinder $classLikeParsedNodesFinder,
|
||||
string $interfacePattern = '#(.*?)#'
|
||||
) {
|
||||
$this->interfacePattern = $interfacePattern;
|
||||
$this->renamedClassesCollector = $renamedClassesCollector;
|
||||
$this->classLikeParsedNodesFinder = $classLikeParsedNodesFinder;
|
||||
}
|
||||
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (count((array) $node->implements) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->implements as $key => $implement) {
|
||||
$implementedInterfaceName = $this->getName($implement);
|
||||
if ($implementedInterfaceName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! Strings::match($implementedInterfaceName, $this->interfacePattern)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// is interface in /vendor? probably useful
|
||||
$classFileLocation = $this->resolveClassFileLocation($implementedInterfaceName);
|
||||
if (Strings::contains($classFileLocation, 'vendor')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$interfaceImplementers = $this->getInterfaceImplementers($implementedInterfaceName);
|
||||
|
||||
// makes sense
|
||||
if (count($interfaceImplementers) > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. replace current interface with one more parent or remove it
|
||||
$this->removeOrReplaceImlementedInterface($implementedInterfaceName, $node, $key);
|
||||
|
||||
// 2. remove file if not in /vendor
|
||||
$this->removeInterfaceFile($implementedInterfaceName, $classFileLocation);
|
||||
|
||||
// 3. replace interface with explicit current class
|
||||
$this->replaceName($node, $implementedInterfaceName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Remove interface, that are added just for its sake, but nowhere useful', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass implements OnlyHereUsedInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface OnlyHereUsedInterface
|
||||
{
|
||||
}
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function __construct(OnlyHereUsedInterface $onlyHereUsed)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function __construct(SomeClass $onlyHereUsed)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getInterfaceImplementers(string $interfaceName): array
|
||||
{
|
||||
return array_filter(
|
||||
get_declared_classes(),
|
||||
function (string $className) use ($interfaceName): bool {
|
||||
return in_array($interfaceName, class_implements($className), true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function getParentInterfaceIfFound(string $implementedInterfaceName): ?string
|
||||
{
|
||||
$interfaceReflection = new ReflectionClass($implementedInterfaceName);
|
||||
|
||||
// get first parent interface
|
||||
return $interfaceReflection->getInterfaceNames()[0] ?? null;
|
||||
}
|
||||
|
||||
private function removeInterfaceFile(string $interfaceName, string $classFileLocation): void
|
||||
{
|
||||
if (StaticPHPUnitEnvironment::isPHPUnitRun()) {
|
||||
$interface = $this->classLikeParsedNodesFinder->findInterface($interfaceName);
|
||||
if ($interface instanceof Interface_) {
|
||||
$this->removeNode($interface);
|
||||
}
|
||||
} else {
|
||||
$smartFileInfo = new SmartFileInfo($classFileLocation);
|
||||
$this->removeFile($smartFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceName(Class_ $class, string $implementedInterfaceName): void
|
||||
{
|
||||
$className = $this->getName($class);
|
||||
if ($className === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->renamedClassesCollector->addClassRename($implementedInterfaceName, $className);
|
||||
}
|
||||
|
||||
private function resolveClassFileLocation(string $implementedInterfaceName)
|
||||
{
|
||||
$deadInterfaceReflection = new ReflectionClass($implementedInterfaceName);
|
||||
return $deadInterfaceReflection->getFileName();
|
||||
}
|
||||
|
||||
private function removeOrReplaceImlementedInterface(string $implementedInterfaceName, Class_ $class, int $key): void
|
||||
{
|
||||
$parentInterface = $this->getParentInterfaceIfFound($implementedInterfaceName);
|
||||
if ($parentInterface !== null) {
|
||||
$class->implements[$key] = new FullyQualified($parentInterface);
|
||||
} else {
|
||||
unset($class->implements[$key]);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\Class_\RemoveUselessJustForSakeInterfaceRector\Fixture;
|
||||
|
||||
class SomeClass implements OnlyHereUsedInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface OnlyHereUsedInterface
|
||||
{
|
||||
}
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function __construct(OnlyHereUsedInterface $onlyHereUsed)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\Class_\RemoveUselessJustForSakeInterfaceRector\Fixture;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function __construct(\Rector\Restoration\Tests\Rector\Class_\RemoveUselessJustForSakeInterfaceRector\Fixture\SomeClass $onlyHereUsed)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\Class_\RemoveUselessJustForSakeInterfaceRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Restoration\Rector\Class_\RemoveUselessJustForSakeInterfaceRector;
|
||||
|
||||
final class RemoveUselessJustForSakeInterfaceRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return RemoveUselessJustForSakeInterfaceRector::class;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user