Skip to content

Commit adf1204

Browse files
committed
feat: implement removeImports method
refs: #60
1 parent 286595e commit adf1204

10 files changed

Lines changed: 284 additions & 9 deletions

src/Builders/PHPFileBuilder.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use RonasIT\Larabuilder\Visitors\PropertyVisitors\AddArrayPropertyItem;
1717
use RonasIT\Larabuilder\Visitors\PropertyVisitors\RemoveArrayPropertyItem;
1818
use RonasIT\Larabuilder\Visitors\PropertyVisitors\SetProperty;
19+
use RonasIT\Larabuilder\Visitors\RemoveImports;
1920

2021
class PHPFileBuilder
2122
{
@@ -69,6 +70,13 @@ public function addImports(array $imports): self
6970
return $this;
7071
}
7172

73+
public function removeImports(array $imports, bool $force = false): self
74+
{
75+
$this->traverser->addVisitor(new RemoveImports($imports, $force));
76+
77+
return $this;
78+
}
79+
7280
public function addTraits(array $traits): self
7381
{
7482
$this->traverser->addVisitor(new AddTraits($traits));

src/Visitors/AddImports.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpParser\Node;
66
use PhpParser\Node\Name;
7-
use PhpParser\Node\Stmt\Namespace_;
87
use PhpParser\Node\Stmt\Use_;
98
use PhpParser\Node\Stmt\UseUse;
109

@@ -31,14 +30,7 @@ public function leaveNode(Node $node): Node
3130

3231
public function afterTraverse(array $nodes): ?array
3332
{
34-
$targetNamespace = array_find($nodes, fn ($node) => $node instanceof Namespace_);
35-
36-
if (!is_null($targetNamespace)) {
37-
/** @var Namespace_ $targetNamespace */
38-
$targetNodes = &$targetNamespace->stmts;
39-
} else {
40-
$targetNodes = &$nodes;
41-
}
33+
$targetNodes = &$this->getNamespaceStatements($nodes);
4234

4335
$this->insertNodes($targetNodes);
4436

src/Visitors/BaseNodeVisitorAbstract.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,15 @@ protected function isSubsequence(array $haystackStatements, array $needleStateme
204204

205205
return false;
206206
}
207+
208+
protected function &getNamespaceStatements(array &$nodes): array
209+
{
210+
$targetNamespace = array_find($nodes, fn ($node) => $node instanceof Namespace_);
211+
212+
if (!is_null($targetNamespace)) {
213+
return $targetNamespace->stmts;
214+
}
215+
216+
return $nodes;
217+
}
207218
}

src/Visitors/RemoveImports.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Visitors;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Name;
7+
use PhpParser\Node\Stmt\Use_;
8+
use PhpParser\Node\UseItem;
9+
use PhpParser\NodeFinder;
10+
use RonasIT\Larabuilder\Nodes\PreformattedCode;
11+
12+
class RemoveImports extends BaseNodeVisitorAbstract
13+
{
14+
protected array $allowedParentNodesTypes = self::ANY_TYPE;
15+
16+
protected NodeFinder $nodeFinder;
17+
18+
public function __construct(
19+
protected array $imports,
20+
protected bool $force = false,
21+
) {
22+
$this->nodeFinder = new NodeFinder();
23+
}
24+
25+
public function afterTraverse(array $nodes): ?array
26+
{
27+
$targetNodes = &$this->getNamespaceStatements($nodes);
28+
29+
foreach ($targetNodes as $node) {
30+
if ($node instanceof Use_) {
31+
$node->uses = array_filter($node->uses, fn (UseItem $useItem) => !$this->shouldRemove($useItem, $targetNodes));
32+
}
33+
}
34+
35+
$targetNodes = array_filter($targetNodes, fn ($node) => !($node instanceof Use_) || !empty($node->uses));
36+
37+
return $nodes;
38+
}
39+
40+
protected function shouldRemove(UseItem $useItem, array $targetNodes): bool
41+
{
42+
if (!in_array($useItem->name->toString(), $this->imports)) {
43+
return false;
44+
}
45+
46+
if ($this->force) {
47+
return true;
48+
}
49+
50+
$resolvedName = $useItem->alias?->name ?? $useItem->name->getLast();
51+
52+
return !$this->isImportUsed($resolvedName, $targetNodes);
53+
}
54+
55+
protected function isImportUsed(string $importName, array $targetNodes): bool
56+
{
57+
$nodesWithoutImports = array_filter($targetNodes, fn ($node) => !($node instanceof Use_));
58+
59+
if ($this->hasUsageOf($importName, $nodesWithoutImports)) {
60+
return true;
61+
}
62+
63+
$preformattedNodes = $this->nodeFinder->find($nodesWithoutImports, fn (Node $node) => $node instanceof PreformattedCode);
64+
65+
foreach ($preformattedNodes as $preformattedNode) {
66+
/** @var PreformattedCode $preformattedNode */
67+
if ($this->hasUsageOf($importName, $preformattedNode->code)) {
68+
return true;
69+
}
70+
}
71+
72+
return false;
73+
}
74+
75+
protected function hasUsageOf(string $name, array $nodes): bool
76+
{
77+
return !empty($this->nodeFinder->find($nodes, fn (Node $node) => $node instanceof Name && $node->toString() === $name));
78+
}
79+
}

tests/PHPFileBuilderTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,75 @@ public function testInsertCodeToMethodNotClassTraitEnum(): void
507507
->save();
508508
}
509509

510+
public function testRemoveImportsUnused(): void
511+
{
512+
$this->mockNativeFunction(
513+
'RonasIT\Larabuilder\Builders',
514+
$this->callFileGetContent('some_file_path.php', 'remove_imports_from_class.php'),
515+
$this->callFilePutContent('some_file_path.php', 'remove_unused_import.php'),
516+
);
517+
518+
new PHPFileBuilder('some_file_path.php')
519+
->removeImports([
520+
'App\Service\UserService',
521+
'App\Support\Traits\SecondTrait',
522+
])
523+
->save();
524+
}
525+
526+
public function testRemoveImportsUsedSkipped(): void
527+
{
528+
$this->mockNativeFunction(
529+
'RonasIT\Larabuilder\Builders',
530+
$this->callFileGetContent('some_file_path.php', 'remove_imports_from_class.php'),
531+
$this->callFilePutContent('some_file_path.php', 'remove_imports_from_class_unchanged.php'),
532+
);
533+
534+
new PHPFileBuilder('some_file_path.php')
535+
->removeImports([
536+
'App\SomeClass',
537+
'App\Models\User',
538+
'RonasIT\Support\Traits\SecondTrait',
539+
])
540+
->save();
541+
}
542+
543+
public function testRemoveImportsForce(): void
544+
{
545+
$this->mockNativeFunction(
546+
'RonasIT\Larabuilder\Builders',
547+
$this->callFileGetContent('some_file_path.php', 'remove_imports_from_class.php'),
548+
$this->callFilePutContent('some_file_path.php', 'remove_imports_force.php'),
549+
);
550+
551+
new PHPFileBuilder('some_file_path.php')
552+
->removeImports([
553+
'App\SomeClass',
554+
'App\Models\User',
555+
'App\Support\Traits\SecondTrait',
556+
'App\Service\UserService',
557+
'App\Support\Classname',
558+
], force: true)
559+
->save();
560+
}
561+
562+
public function testRemoveImportsAfterChanges(): void
563+
{
564+
$this->mockNativeFunction(
565+
'RonasIT\Larabuilder\Builders',
566+
$this->callFileGetContent('some_file_path.php', 'remove_imports_from_class.php'),
567+
$this->callFilePutContent('some_file_path.php', 'remove_imports_after_changes.php'),
568+
);
569+
570+
new PHPFileBuilder('some_file_path.php')
571+
->insertCodeToMethod('someMethod', 'app(UserService::class)->doSomething();')
572+
->removeImports([
573+
'App\Service\UserService',
574+
'App\Support\Classname',
575+
])
576+
->save();
577+
}
578+
510579
public function testInsertCodeToMethodWhenMethodNotExist(): void
511580
{
512581
$this->mockNativeFunction(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Tests\Support;
4+
5+
use App\SomeClass;
6+
use App\Models\User;
7+
use App\Service\UserService;
8+
use RonasIT\Support\Traits\SecondTrait as SomeTrait;
9+
use App\Support\Traits\SecondTrait as UnusedTrait, App\Support\Classname;
10+
11+
class SomeClass extends SomeClass
12+
{
13+
use SomeTrait;
14+
15+
protected string $property;
16+
17+
public function __construct(): void
18+
{
19+
}
20+
21+
public function someMethod(User $user): void
22+
{
23+
}
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Tests\Support;
4+
5+
use App\SomeClass;
6+
use App\Models\User;
7+
use App\Service\UserService;
8+
use RonasIT\Support\Traits\SecondTrait as SomeTrait;
9+
use App\Support\Traits\SecondTrait as UnusedTrait;
10+
11+
class SomeClass extends SomeClass
12+
{
13+
use SomeTrait;
14+
15+
protected string $property;
16+
17+
public function __construct(): void
18+
{
19+
}
20+
21+
public function someMethod(User $user): void
22+
{
23+
app(UserService::class)->doSomething();
24+
}
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Tests\Support;
4+
5+
use RonasIT\Support\Traits\SecondTrait as SomeTrait;
6+
7+
class SomeClass extends SomeClass
8+
{
9+
use SomeTrait;
10+
11+
protected string $property;
12+
13+
public function __construct(): void
14+
{
15+
}
16+
17+
public function someMethod(User $user): void
18+
{
19+
}
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Tests\Support;
4+
5+
use App\SomeClass;
6+
use App\Models\User;
7+
use App\Service\UserService;
8+
use RonasIT\Support\Traits\SecondTrait as SomeTrait;
9+
use App\Support\Traits\SecondTrait as UnusedTrait, App\Support\Classname;
10+
11+
class SomeClass extends SomeClass
12+
{
13+
use SomeTrait;
14+
15+
protected string $property;
16+
17+
public function __construct(): void
18+
{
19+
}
20+
21+
public function someMethod(User $user): void
22+
{
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace RonasIT\Larabuilder\Tests\Support;
4+
5+
use App\SomeClass;
6+
use App\Models\User;
7+
use RonasIT\Support\Traits\SecondTrait as SomeTrait;
8+
use App\Support\Classname;
9+
10+
class SomeClass extends SomeClass
11+
{
12+
use SomeTrait;
13+
14+
protected string $property;
15+
16+
public function __construct(): void
17+
{
18+
}
19+
20+
public function someMethod(User $user): void
21+
{
22+
}
23+
}

0 commit comments

Comments
 (0)