modifyComments()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 21
rs 9.6111
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 RectorPrefix202601\Nette\Utils\Strings;
0 ignored issues
show
Bug introduced by
The type RectorPrefix202601\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 is_array;
32
use function preg_match;
33
use function strtolower;
34
35
use const false;
36
use const true;
37
38
final class RemoveNonConflictingAliasInUseStatementRector extends AbstractRector
39
{
40
    /**
41
     * @throws PoorDocumentationException
42
     */
43
    public function getRuleDefinition(): RuleDefinition
44
    {
45
        return new RuleDefinition('Remove non-conflicting alias in use statement when an alias exists for no conflicting reason', [
46
            new CodeSample(
47
                <<<'CODE_SAMPLE'
48
                    use App\Bar as AppBar;
49
                    CODE_SAMPLE,
50
                <<<'CODE_SAMPLE'
51
                    use App\Bar;
52
                    CODE_SAMPLE
53
            ),
54
        ]);
55
    }
56
57
    /**
58
     * @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...
59
     */
60
    public function getNodeTypes(): array
61
    {
62
        return [FileNode::class, Namespace_::class];
63
    }
64
65
    public function refactor(Node $node): FileNode|Node|Namespace_|null
66
    {
67
        if ($node instanceof FileNode && $node->isNamespaced()) {
68
            // handle in Namespace_ node
69
            return null;
70
        }
71
        $hasChanged = false;
72
73
        foreach ($node->stmts as $stmt) {
74
            if (! $stmt instanceof Use_) {
75
                continue;
76
            }
77
78
            if (count($stmt->uses) !== 1) {
79
                continue;
80
            }
81
82
            if (! isset($stmt->uses[0])) {
83
                continue;
84
            }
85
            $aliasName = $stmt->uses[0]->alias instanceof Identifier ? $stmt->uses[0]->alias->toString() : null;
86
87
            if ($aliasName === null) {
88
                continue;
89
            }
90
91
            $useNameToCheck   = $stmt->uses[0]->name->toString();
92
            $aliasUseLastName = Strings::after($useNameToCheck, '\\', -1) ?? $useNameToCheck;
93
94
            foreach ($node->stmts as $compareStmt) {
95
                if (
96
                    (
97
                        $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...
98
                        || $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...
99
                        || $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...
100
                        || $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...
101
                    )
102
                    && $compareStmt->name?->name !== null
103
                    // Ensure the alias's class name does not match the class/interface/trait/enum class name
104
                    && strtolower($compareStmt->name->name ?? '') === strtolower($aliasUseLastName)
105
                ) {
106
                    // If it did this alias is required and we should move onto the next alias
107
                    continue 2;
108
                }
109
110
                if ($compareStmt === $stmt) {
111
                    continue;
112
                }
113
114
                if (! $compareStmt instanceof Use_) {
115
                    continue;
116
                }
117
118
                if (count($compareStmt->uses) !== 1) {
119
                    continue;
120
                }
121
122
                if (! isset($compareStmt->uses[0])) {
123
                    continue;
124
                }
125
126
                $use          = $compareStmt->uses[0]->name->toString();
127
                $lastName     = Strings::after($use, '\\', -1) ?? $use;
128
                $useAliasName = $stmt->uses[0]->alias instanceof Identifier ? $stmt->uses[0]->alias->toString() : null;
129
                $useHasAlias  = $useAliasName !== null;
130
131
                if (
132
                    // Ensure the alias is not the same as the class name of another use statement
133
                    strtolower($lastName) === strtolower($aliasName)
134
                    // Ensure the alias's class name is not the same as the class name of another use statement that has no alias
135
                    || (! $useHasAlias && strtolower($lastName) === strtolower($aliasUseLastName))
136
                    // Ensure the alias is not the same as the alias of another use statement that has an alias
137
                    || ($useHasAlias && strtolower($useAliasName) === strtolower($aliasName))
138
                ) {
139
                    // If it matched then this alias is required and we should move onto the next alias
140
                    continue 2;
141
                }
142
            }
143
144
            $stmt->uses[0]->alias = null;
145
            $hasChanged           = true;
146
147
            $nodeFinder = new NodeFinder();
148
            // Get all nodes
149
            $allNodes = $nodeFinder->findInstanceOf($node, Node::class);
150
151
            foreach ($allNodes as $allNode) {
152
                $this->modifyComments($allNode, $aliasName, $aliasUseLastName);
153
                $this->modifyNodeClassName($allNode, 'name', $aliasName, $aliasUseLastName);
154
                $this->modifyNodeClassName($allNode, 'class', $aliasName, $aliasUseLastName);
155
                $this->modifyNodeClassName($allNode, 'extends', $aliasName, $aliasUseLastName);
156
                $this->modifyNodeClassName($allNode, 'type', $aliasName, $aliasUseLastName);
157
                $this->modifyNodeClassName($allNode, 'returnType', $aliasName, $aliasUseLastName);
158
                $this->modifyNodes($allNode, 'implements', $aliasName, $aliasUseLastName);
159
                $this->modifyNodes($allNode, 'extends', $aliasName, $aliasUseLastName);
160
                $this->modifyNodes($allNode, 'traits', $aliasName, $aliasUseLastName);
161
            }
162
        }
163
164
        if ($hasChanged) {
165
            return $node;
166
        }
167
168
        return null;
169
    }
170
171
    private function modifyComments(Node $node, string $alias, string $className): void
172
    {
173
        // Docblocks are stored in the 'comments' attribute
174
        $comments = $node->getAttribute('comments');
175
176
        if (! empty($comments)) {
177
            $newComments = [];
178
179
            foreach ($comments as $comment) {
180
                if ($comment instanceof Doc && preg_match("/(\W)$alias(\W)/", $comment->getText()) === 1) {
181
                    $newComments[] = new Doc(
182
                        text: preg_replace("/(\W)$alias(\W)/", "$1$className$2", $comment->getText()),
183
                    );
184
185
                    continue;
186
                }
187
                $newComments[] = $comment;
188
            }
189
190
            // Set the filtered comments back to the node
191
            $node->setAttribute('comments', $newComments);
192
        }
193
    }
194
195
    private function modifyNodeClassName(Node $node, string $property, string $alias, string $className): void
196
    {
197
        $NameNode = $node->$property ?? null;
198
199
        if ($NameNode instanceof FullyQualified && $NameNode->getAttribute('originalName')?->name === $alias) {
200
            $newNameNode = new FullyQualified($NameNode->name);
201
            $newNameNode->setAttribute('originalName', $className);
202
203
            $node->$property = $newNameNode;
204
        }
205
    }
206
207
    private function modifyNodes(Node $node, string $property, string $alias, string $className): void
208
    {
209
        $nameNodes = $node->$property ?? null;
210
211
        if (! is_array($nameNodes)) {
212
            return;
213
        }
214
215
        $newNameNodes = [];
216
217
        foreach ($nameNodes as $nameNode) {
218
            if ($nameNode instanceof FullyQualified && $nameNode->getAttribute('originalName')?->name === $alias) {
219
                $newClass = new FullyQualified($nameNode->name);
220
                $newClass->setAttribute('originalName', $className);
221
222
                $newNameNodes[] = $newClass;
223
224
                continue;
225
            }
226
227
            $newNameNodes[] = $nameNode;
228
        }
229
230
        $node->$property = $newNameNodes;
231
    }
232
}
233