Passed
Push — master ( 5a1894...bbd509 )
by Melech
01:45
created

modifyNodeClassName()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 2
nop 4
dl 0
loc 9
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Rector\CodingStyle\Rector\Stmt;
15
16
use PhpParser\Comment\Doc;
0 ignored issues
show
Bug introduced by
The type PhpParser\Comment\Doc was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use PhpParser\Node;
0 ignored issues
show
Bug introduced by
The type PhpParser\Node was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use PhpParser\Node\Identifier;
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Identifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use PhpParser\Node\Name\FullyQualified;
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Name\FullyQualified was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use PhpParser\Node\Stmt\Namespace_;
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Namespace_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use PhpParser\Node\Stmt\Use_;
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Use_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use PhpParser\NodeFinder;
0 ignored issues
show
Bug introduced by
The type PhpParser\NodeFinder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Rector\PhpParser\Node\FileNode;
0 ignored issues
show
Bug introduced by
The type Rector\PhpParser\Node\FileNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Rector\Rector\AbstractRector;
0 ignored issues
show
Bug introduced by
The type Rector\Rector\AbstractRector was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use RectorPrefix202512\Nette\Utils\Strings;
0 ignored issues
show
Bug introduced by
The type RectorPrefix202512\Nette\Utils\Strings was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use Symplify\RuleDocGenerator\Exception\PoorDocumentationException;
0 ignored issues
show
Bug introduced by
The type Symplify\RuleDocGenerato...rDocumentationException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
0 ignored issues
show
Bug introduced by
The type Symplify\RuleDocGenerato...t\CodeSample\CodeSample was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
0 ignored issues
show
Bug introduced by
The type Symplify\RuleDocGenerato...ueObject\RuleDefinition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
30
use function count;
31
use function file_put_contents;
32
use function is_array;
33
use function preg_match;
34
use function strtolower;
35
36
use const false;
37
use const true;
38
39
final class RemoveNonConflictingAliasInUseStatementRector extends AbstractRector
40
{
41
    /**
42
     * @throws PoorDocumentationException
43
     */
44
    public function getRuleDefinition(): RuleDefinition
45
    {
46
        return new RuleDefinition('Remove non-conflicting alias in use statement when an alias exists for no conflicting reason', [
47
            new CodeSample(
48
                <<<'CODE_SAMPLE'
49
                    use App\Bar as AppBar;
50
                    CODE_SAMPLE,
51
                <<<'CODE_SAMPLE'
52
                    use App\Bar;
53
                    CODE_SAMPLE
54
            ),
55
        ]);
56
    }
57
58
    /**
59
     * @return array<class-string<Node>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<Node>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<Node>>.
Loading history...
60
     */
61
    public function getNodeTypes(): array
62
    {
63
        return [FileNode::class, Namespace_::class];
64
    }
65
66
    public function refactor(Node $node): FileNode|Node|Namespace_|null
67
    {
68
        if ($node instanceof FileNode && $node->isNamespaced()) {
69
            // handle in Namespace_ node
70
            return null;
71
        }
72
        $hasChanged = false;
73
74
        foreach ($node->stmts as $stmt) {
75
            if (! $stmt instanceof Use_) {
76
                continue;
77
            }
78
79
            if (count($stmt->uses) !== 1) {
80
                continue;
81
            }
82
83
            if (! isset($stmt->uses[0])) {
84
                continue;
85
            }
86
            $aliasName = $stmt->uses[0]->alias instanceof Identifier ? $stmt->uses[0]->alias->toString() : null;
87
88
            if ($aliasName === null) {
89
                continue;
90
            }
91
92
            $useName          = $stmt->uses[0]->name->toString();
93
            $aliasUseLastName = Strings::after($useName, '\\', -1) ?? $useName;
94
95
            foreach ($node->stmts as $compareStmt) {
96
                if (
97
                    (
98
                        $compareStmt instanceof Node\Stmt\Class_
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Class_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
99
                        || $compareStmt instanceof Node\Stmt\Interface_
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Interface_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
100
                        || $compareStmt instanceof Node\Stmt\Trait_
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Trait_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
101
                        || $compareStmt instanceof Node\Stmt\Enum_
0 ignored issues
show
Bug introduced by
The type PhpParser\Node\Stmt\Enum_ was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
102
                    )
103
                    && $compareStmt?->name?->name !== null
104
                    && strtolower($compareStmt->name->name ?? '') === strtolower($aliasUseLastName)
105
                ) {
106
                    continue 2;
107
                }
108
109
                if ($compareStmt === $stmt) {
110
                    continue;
111
                }
112
113
                if (! $compareStmt instanceof Use_) {
114
                    continue;
115
                }
116
117
                if (count($compareStmt->uses) !== 1) {
118
                    continue;
119
                }
120
121
                if (! isset($compareStmt->uses[0])) {
122
                    continue;
123
                }
124
125
                $use      = $compareStmt->uses[0]->name->toString();
126
                $lastName = Strings::after($use, '\\', -1) ?? $use;
127
128
                if (strtolower($lastName) === strtolower($aliasName) || strtolower($lastName) === strtolower($aliasUseLastName)) {
129
                    continue 2;
130
                }
131
            }
132
133
            $stmt->uses[0]->alias = null;
134
            $hasChanged           = true;
135
136
            $nodeFinder = new NodeFinder();
137
            $allNodes   = $nodeFinder->findInstanceOf($node, Node::class);
138
139
            foreach ($allNodes as $allNode) {
140
                $this->modifyComments($allNode, $aliasName, $aliasUseLastName);
141
                $this->modifyNodeClassName($allNode, 'name', $aliasName, $aliasUseLastName);
142
                $this->modifyNodeClassName($allNode, 'class', $aliasName, $aliasUseLastName);
143
                $this->modifyNodeClassName($allNode, 'extends', $aliasName, $aliasUseLastName);
144
                $this->modifyNodeClassName($allNode, 'type', $aliasName, $aliasUseLastName);
145
                $this->modifyNodeClassName($allNode, 'returnType', $aliasName, $aliasUseLastName);
146
                $this->modifyNodes($allNode, 'implements', $aliasName, $aliasUseLastName);
147
                $this->modifyNodes($allNode, 'extends', $aliasName, $aliasUseLastName);
148
                $this->modifyNodes($allNode, 'traits', $aliasName, $aliasUseLastName);
149
            }
150
151
            // if ($aliasName === 'EnumTrait') {
152
            //     file_put_contents(__DIR__ . 'traituseexample.json', json_encode($node));
153
            // }
154
        }
155
156
        if ($hasChanged) {
157
            return $node;
158
        }
159
160
        return null;
161
    }
162
163
    private function modifyComments(Node $node, string $alias, string $className): void
164
    {
165
        // Docblocks are stored in the 'comments' attribute
166
        $comments = $node->getAttribute('comments');
167
168
        if (! empty($comments)) {
169
            $newComments = [];
170
171
            foreach ($comments as $comment) {
172
                if ($comment instanceof Doc && preg_match("/(\W)$alias(\W)/", $comment->getText()) === 1) {
173
                    $newComments[] = new Doc(
174
                        text: preg_replace("/(\W)$alias(\W)/", "$1$className$2", $comment->getText()),
175
                    );
176
177
                    continue;
178
                }
179
                $newComments[] = $comment;
180
            }
181
182
            // Set the filtered comments back to the node
183
            $node->setAttribute('comments', $newComments);
184
        }
185
    }
186
187
    private function modifyNodeClassName(Node $node, string $property, string $alias, string $className): void
188
    {
189
        $NameNode = $node->$property ?? null;
190
191
        if ($NameNode instanceof FullyQualified && $NameNode->getAttribute('originalName')?->name === $alias) {
192
            $newNameNode = new FullyQualified($NameNode->name);
193
            $newNameNode->setAttribute('originalName', $className);
194
195
            $node->$property = $newNameNode;
196
        }
197
    }
198
199
    private function modifyNodes(Node $node, string $property, string $alias, string $className): void
200
    {
201
        $nameNodes = $node->$property ?? null;
202
203
        if (! is_array($nameNodes)) {
204
            return;
205
        }
206
207
        $newNameNodes = [];
208
209
        foreach ($nameNodes as $nameNode) {
210
            if ($nameNode instanceof FullyQualified && $nameNode->getAttribute('originalName')?->name === $alias) {
211
                $newClass = new FullyQualified($nameNode->name);
212
                $newClass->setAttribute('originalName', $className);
213
214
                $newNameNodes[] = $newClass;
215
216
                continue;
217
            }
218
219
            $newNameNodes[] = $nameNode;
220
        }
221
222
        $node->$property = $newNameNodes;
223
    }
224
}
225