[Removing] Add RemoveNamespaceRector (#2013)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-04-06 19:32:48 +07:00 committed by GitHub
parent 08a3243799
commit be530c5e2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 439 additions and 2 deletions

View File

@ -1,4 +1,4 @@
# 508 Rules Overview
# 509 Rules Overview
<br>
@ -76,7 +76,7 @@
- [Privatization](#privatization) (10)
- [Removing](#removing) (6)
- [Removing](#removing) (7)
- [RemovingStatic](#removingstatic) (1)
@ -9160,6 +9160,37 @@ return static function (ContainerConfigurator $containerConfigurator): void {
<br>
### RemoveNamespaceRector
Remove namespace by configured namespace names
:wrench: **configure it!**
- class: [`Rector\Removing\Rector\Namespace_\RemoveNamespaceRector`](../rules/Removing/Rector/Namespace_/RemoveNamespaceRector.php)
```php
use Rector\Removing\Rector\Namespace_\RemoveNamespaceRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(RemoveNamespaceRector::class)
->configure(['App']);
};
```
```diff
-namespace App;
class SomeClass
{
}
```
<br>
### RemoveParentRector
Removes extends class by name

View File

@ -0,0 +1,17 @@
<?php
namespace App;
class Fixture
{
}
?>
-----
<?php
class Fixture
{
}
?>

View File

@ -0,0 +1,30 @@
<?php
namespace App
{
class HasNextNamespace
{
}
}
namespace
{
echo 'test';
}
?>
-----
<?php
namespace {
class HasNextNamespace
{
}
}
namespace
{
echo 'test';
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace App
{
use stdClass;
class HasNextNamespaceNonCompount
{
}
}
namespace
{
echo 'test';
}
?>
-----
<?php
namespace {
class HasNextNamespaceNonCompount
{
}
}
namespace
{
echo 'test';
}
?>

View File

@ -0,0 +1,30 @@
<?php
namespace
{
echo 'test';
}
namespace App
{
class HasPrevNamespace
{
}
}
?>
-----
<?php
namespace
{
echo 'test';
}
namespace {
class HasPrevNamespace
{
}
}
?>

View File

@ -0,0 +1,18 @@
<?php
namespace App;
use A\B\C, stdClass, D\E\F;
class RemoveNamespaceCombineCompountNonCompount
{
}
?>
-----
<?php
use A\B\C, D\E\F;
class RemoveNamespaceCombineCompountNonCompount
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace App;
?>
-----
<?php

View File

@ -0,0 +1,18 @@
<?php
namespace App;
use A\B\C;
class RemoveNamespaceUseCompountuse
{
}
?>
-----
<?php
use A\B\C;
class RemoveNamespaceUseCompountuse
{
}

View File

@ -0,0 +1,19 @@
<?php
namespace App;
use stdClass;
class RemoveNamespaceUseNonCompountuse
{
}
?>
-----
<?php
class RemoveNamespaceUseNonCompountuse
{
}
?>

View File

@ -0,0 +1,18 @@
<?php
namespace App;
use stdClass as std;
class RemoveNamespaceUseNonCompountuseWithAlias
{
}
?>
-----
<?php
use stdClass as std;
class RemoveNamespaceUseNonCompountuseWithAlias
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace DifferentNamespace;
class SkipDifferentNamespace
{
}

View File

@ -0,0 +1,3 @@
<?php
class NoNamespace{}

View File

@ -0,0 +1,8 @@
<?php
namespace
{
class NullNamespace
{
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Removing\Rector\Namespace_\RemoveNamespaceRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveNamespaceRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
use Rector\Removing\Rector\Namespace_\RemoveNamespaceRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(RemoveNamespaceRector::class)
->configure(['App']);
};

View File

@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace Rector\Removing\Rector\Namespace_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;
/**
* @see \Rector\Tests\Removing\Rector\Namespace_\RemoveNamespaceRector\RemoveNamespaceRectorTest
*/
final class RemoveNamespaceRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string[]
*/
private array $removeNamespaces = [];
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove namespace by configured namespace names', [
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
namespace App;
class SomeClass
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
}
CODE_SAMPLE
,
['App']
),
]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Namespace_::class];
}
/**
* @param Namespace_ $node
* @return Stmt[]|Node|null
*/
public function refactor(Node $node): array|Node|null
{
$namespaceName = $this->nodeNameResolver->getName($node);
if ($namespaceName === null) {
return null;
}
foreach ($this->removeNamespaces as $removeNamespace) {
if ($removeNamespace !== $namespaceName) {
continue;
}
return $this->processRemoveNamespace($node);
}
return null;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
Assert::allString($configuration);
$this->removeNamespaces = $configuration;
}
/**
* @return Stmt[]|Namespace_
*/
private function processRemoveNamespace(Namespace_ $namespace): array|Namespace_
{
$stmts = $this->cleanNonCompoundUseName($namespace->stmts);
// has prev or next namespace should just clean namespace name to avoid error
// `Namespace declaration statement has to be the very first statement` ref https://3v4l.org/qUMfb
// or `No code may exist outside of namespace {}` ref https://3v4l.org/ct7SR
if ($this->hasMultipleNamespaces($namespace)) {
return new Namespace_(null, $stmts);
}
if ($stmts === []) {
$this->removeNode($namespace);
return $namespace;
}
return $stmts;
}
private function hasMultipleNamespaces(Namespace_ $namespace): bool
{
$prev = $namespace->getAttribute(AttributeKey::PREVIOUS_STATEMENT);
$next = $namespace->getAttribute(AttributeKey::NEXT_NODE);
return $prev instanceof Namespace_ || $next instanceof Namespace_;
}
/**
* @param Stmt[] $stmts
* @return Stmt[]
*/
private function cleanNonCompoundUseName(array $stmts): array
{
foreach ($stmts as $key => $stmt) {
if (! $stmt instanceof Use_) {
continue;
}
$uses = $stmt->uses;
foreach ($uses as $keyUse => $use) {
if ($use->alias instanceof Identifier) {
continue;
}
$useName = ltrim($use->name->toString(), '\\');
if (! str_contains($useName, '\\')) {
unset($uses[$keyUse]);
}
}
if ($uses === []) {
unset($stmts[$key]);
continue;
}
$uses = array_values($uses);
$stmt->uses = $uses;
}
return $stmts;
}
}