-
Notifications
You must be signed in to change notification settings - Fork 19
/
NoImportFromGlobalNamespaceFixer.php
131 lines (106 loc) · 3.93 KB
/
NoImportFromGlobalNamespaceFixer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
declare(strict_types = 1);
namespace PhpCsFixerCustomFixers\Fixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixerCustomFixers\TokenRemover;
final class NoImportFromGlobalNamespaceFixer extends AbstractFixer
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There must be no import from global namespace.',
[new CodeSample('<?php
namespace Foo;
use DateTime;
class Bar {
public function __construct(DateTime $dateTime) {}
}
')]
);
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(T_USE);
}
public function isRisky(): bool
{
return false;
}
public function getPriority(): int
{
return 0;
}
public function fix(\SplFileInfo $file, Tokens $tokens): void
{
foreach (\array_reverse((new NamespacesAnalyzer())->getDeclarations($tokens)) as $namespace) {
$this->fixImports($tokens, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), $namespace->getFullName() === '');
}
}
private function fixImports(Tokens $tokens, int $startIndex, int $endIndex, bool $isInGlobalNamespace): void
{
$imports = [];
for ($index = $startIndex; $index < $endIndex; $index++) {
$token = $tokens[$index];
if ($token->isGivenKind(T_USE)) {
$imports = $this->removeImportFromGlobalNamespace($tokens, $imports, $index);
continue;
}
if ($isInGlobalNamespace) {
continue;
}
if ($token->isGivenKind(T_DOC_COMMENT)) {
$this->updateComment($tokens, $imports, $index);
continue;
}
if (!$token->isGivenKind(T_STRING)) {
continue;
}
$this->updateUsage($tokens, $imports, $index);
}
}
private function removeImportFromGlobalNamespace(Tokens $tokens, array $imports, int $index): array
{
/** @var int $classNameIndex */
$classNameIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$classNameIndex]->isGivenKind(T_NS_SEPARATOR)) {
/** @var int $classNameIndex */
$classNameIndex = $tokens->getNextMeaningfulToken($classNameIndex);
}
/** @var int $semicolonIndex */
$semicolonIndex = $tokens->getNextMeaningfulToken($classNameIndex);
if ($tokens[$semicolonIndex]->getContent() === ';') {
$imports[] = $tokens[$classNameIndex]->getContent();
$tokens->clearRange($index, $semicolonIndex);
TokenRemover::removeWithLinesIfPossible($tokens, $semicolonIndex);
}
return $imports;
}
private function updateComment(Tokens $tokens, array $imports, int $index): void
{
$content = $tokens[$index]->getContent();
foreach ($imports as $import) {
$content = Preg::replace(\sprintf('/\b(?<!\\\\)%s\b/', $import), '\\' . $import, $content);
}
if ($content !== $tokens[$index]->getContent()) {
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
}
}
private function updateUsage(Tokens $tokens, array $imports, int $index): void
{
if (!\in_array($tokens[$index]->getContent(), $imports, true)) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind([T_CONST, T_DOUBLE_COLON, T_NS_SEPARATOR, T_OBJECT_OPERATOR, CT::T_USE_TRAIT])) {
return;
}
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}