generated from ibexa/bundle-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IBX-8224: Added rector rule to remove interface & methods implementat…
…ions (#4) For more details see https://issues.ibexa.co/browse/IBX-8224 and #4 Key changes: * Added rector rule to remove interface & methods implementations * [Tests] Added test coverage
- Loading branch information
Showing
9 changed files
with
398 additions
and
0 deletions.
There are no files selected for viewing
126 changes: 126 additions & 0 deletions
126
src/lib/Rule/Internal/RemoveInterfaceWithMethodsRector.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
* @license For full copyright and license information view LICENSE file distributed with this source code. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Ibexa\Rector\Rule\Internal; | ||
|
||
use Ibexa\Rector\Visitor\DependentMethodCallRemovingVisitor; | ||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\ClassMethod; | ||
use PhpParser\NodeTraverser; | ||
use Rector\Contract\Rector\ConfigurableRectorInterface; | ||
use Rector\Rector\AbstractRector; | ||
use Rector\Removing\Rector\Class_\RemoveInterfacesRector; | ||
use ReflectionClass; | ||
use ReflectionMethod; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
final class RemoveInterfaceWithMethodsRector extends AbstractRector implements ConfigurableRectorInterface | ||
{ | ||
private RemoveInterfacesRector $removeInterfacesRector; | ||
|
||
/** @var class-string[] */ | ||
private array $interfacesToRemove; | ||
|
||
public function __construct(RemoveInterfacesRector $removeInterfacesRector) | ||
{ | ||
$this->removeInterfacesRector = $removeInterfacesRector; | ||
} | ||
|
||
/** | ||
* @throws \Exception | ||
*/ | ||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('Remove Interface implementation with all its methods', [new ConfiguredCodeSample( | ||
<<<'CODE_SAMPLE' | ||
class SomeClassWithInterface implements InterfaceToRemove | ||
{ | ||
public function interfaceMethod(): array | ||
{ | ||
return ['smth']; | ||
} | ||
} | ||
CODE_SAMPLE | ||
, | ||
<<<'CODE_SAMPLE' | ||
class SomeClassWithInterface | ||
{ | ||
} | ||
CODE_SAMPLE | ||
, | ||
['var_dump'] | ||
)]); | ||
} | ||
|
||
/** | ||
* @return array<class-string<\PhpParser\Node>> | ||
*/ | ||
public function getNodeTypes(): array | ||
{ | ||
return $this->removeInterfacesRector->getNodeTypes(); | ||
} | ||
|
||
/** | ||
* @param \PhpParser\Node\Stmt\Class_ $node | ||
*/ | ||
public function refactor(Node $node): ?int | ||
{ | ||
if ($node->implements === []) { | ||
return null; | ||
} | ||
|
||
foreach ($node->implements as $implement) { | ||
if ($this->isNames($implement, $this->interfacesToRemove)) { | ||
foreach ($this->interfacesToRemove as $interface) { | ||
$ref = new ReflectionClass($interface); | ||
$methods = array_map(static fn (ReflectionMethod $reflectionMethod): string => $reflectionMethod->getName(), $ref->getMethods()); | ||
|
||
// Remove method definition | ||
foreach ($node->stmts as $key => $stmt) { | ||
if ($stmt instanceof ClassMethod && $this->isNames($stmt, $methods)) { | ||
unset($node->stmts[$key]); | ||
} | ||
} | ||
|
||
// Remove method calls, if one of the arguments was removed method | ||
foreach ($node->getMethods() as $classMethod) { | ||
$nodeTraverser = new NodeTraverser(); | ||
$nodeTraverser->addVisitor( | ||
new DependentMethodCallRemovingVisitor( | ||
$this->nodeNameResolver, | ||
$methods | ||
) | ||
); | ||
|
||
if ($classMethod->stmts !== null) { | ||
/** @var array<\PhpParser\Node\Stmt>|null $traversedStmts */ | ||
$traversedStmts = $nodeTraverser->traverse($classMethod->stmts); | ||
$classMethod->stmts = $traversedStmts; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Remove interface | ||
$this->removeInterfacesRector->refactor($node); | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* @param class-string[] $configuration | ||
*/ | ||
public function configure(array $configuration): void | ||
{ | ||
$this->interfacesToRemove = $configuration; | ||
|
||
$this->removeInterfacesRector->configure($configuration); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
* @license For full copyright and license information view LICENSE file distributed with this source code. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Ibexa\Rector\Visitor; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Expr\MethodCall; | ||
use PhpParser\NodeVisitorAbstract; | ||
use Rector\NodeNameResolver\NodeNameResolver; | ||
|
||
/** | ||
* Removes method call, when one of the methods is used as argument to that method. | ||
*/ | ||
final class DependentMethodCallRemovingVisitor extends NodeVisitorAbstract | ||
{ | ||
private NodeNameResolver $nodeNameResolver; | ||
|
||
/** @var string[] */ | ||
private array $methods; | ||
|
||
/** | ||
* @param string[] $methods | ||
*/ | ||
public function __construct(NodeNameResolver $nodeNameResolver, array $methods) | ||
{ | ||
$this->nodeNameResolver = $nodeNameResolver; | ||
$this->methods = $methods; | ||
} | ||
|
||
public function leaveNode(Node $node) | ||
{ | ||
if ($node instanceof MethodCall) { | ||
foreach ($node->getArgs() as $arg) { | ||
$argValue = $arg->value; | ||
if ($argValue instanceof MethodCall && $this->nodeNameResolver->isNames($argValue->name, $this->methods)) { | ||
return $node->var; | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
tests/lib/Rule/Internal/RemoveInterfaceWithMethods/Fixture/SomeInterface.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
* @license For full copyright and license information view LICENSE file distributed with this source code. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture; | ||
|
||
interface SomeInterface | ||
{ | ||
/** | ||
* @return string[] | ||
*/ | ||
public function someMethod(): array; | ||
} |
43 changes: 43 additions & 0 deletions
43
tests/lib/Rule/Internal/RemoveInterfaceWithMethods/Fixture/class_with_extra_methods.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
|
||
class SomeClass implements SomeInterface | ||
{ | ||
public function firstMethod(): void | ||
{ | ||
|
||
} | ||
|
||
public function someMethod(): array | ||
{ | ||
return ['ezplatform:some_old:command_with_interface']; | ||
} | ||
|
||
public function thirdMethod(): void | ||
{ | ||
|
||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
|
||
class SomeClass | ||
{ | ||
public function firstMethod(): void | ||
{ | ||
|
||
} | ||
public function thirdMethod(): void | ||
{ | ||
|
||
} | ||
} | ||
|
||
?> |
23 changes: 23 additions & 0 deletions
23
tests/lib/Rule/Internal/RemoveInterfaceWithMethods/Fixture/class_with_interface.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
|
||
class SomeClass implements SomeInterface | ||
{ | ||
public function someMethod(): array | ||
{ | ||
return ['ezplatform:some_old:command_with_interface']; | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
|
||
class SomeClass | ||
{ | ||
} | ||
|
||
?> |
52 changes: 52 additions & 0 deletions
52
...b/Rule/Internal/RemoveInterfaceWithMethods/Fixture/class_with_removed_method_call.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
|
||
class SomeClass implements SomeInterface | ||
{ | ||
public function firstMethod(): void | ||
{ | ||
|
||
} | ||
|
||
public function configure() | ||
{ | ||
$this->firstMethod()->firstMethod($this->someMethod())->thirdMethod(); | ||
} | ||
|
||
public function someMethod(): array | ||
{ | ||
return ['ezplatform:some_old:command_with_interface']; | ||
} | ||
|
||
public function thirdMethod(): void | ||
{ | ||
|
||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
|
||
class SomeClass | ||
{ | ||
public function firstMethod(): void | ||
{ | ||
|
||
} | ||
public function configure() | ||
{ | ||
$this->firstMethod()->thirdMethod(); | ||
} | ||
public function thirdMethod(): void | ||
{ | ||
|
||
} | ||
} | ||
|
||
?> |
34 changes: 34 additions & 0 deletions
34
tests/lib/Rule/Internal/RemoveInterfaceWithMethods/Fixture/class_with_two_interfaces.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use OtherInterface; | ||
|
||
class SomeClass implements SomeInterface, OtherInterface | ||
{ | ||
public function firstMethod() | ||
{ | ||
|
||
} | ||
|
||
public function someMethod(): array | ||
{ | ||
return ['ezplatform:some_old:command_with_interface']; | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php /** @noinspection ALL */ | ||
|
||
use Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods\Fixture\SomeInterface; | ||
use OtherInterface; | ||
|
||
class SomeClass implements OtherInterface | ||
{ | ||
public function firstMethod() | ||
{ | ||
|
||
} | ||
} | ||
|
||
?> |
37 changes: 37 additions & 0 deletions
37
tests/lib/Rule/Internal/RemoveInterfaceWithMethods/RemoveInterfaceWithMethodsRectorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
/** | ||
* @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
* @license For full copyright and license information view LICENSE file distributed with this source code. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Ibexa\Rector\Tests\Rule\Internal\RemoveInterfaceWithMethods; | ||
|
||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
/** | ||
* @covers \Ibexa\Rector\Rule\Internal\RemoveLegacyClassAliasRector | ||
*/ | ||
final class RemoveInterfaceWithMethodsRectorTest extends AbstractRectorTestCase | ||
{ | ||
/** | ||
* @throws \Rector\Exception\ShouldNotHappenException | ||
*/ | ||
#[DataProvider('provideData')] | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public static function provideData(): \Iterator | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/configured_rule.php'; | ||
} | ||
} |
Oops, something went wrong.