• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHPCSExtra / 15639983570

13 Jun 2025 05:03PM UTC coverage: 99.508% (-0.3%) from 99.85%
15639983570

Pull #367

github

web-flow
Merge e570313e5 into 10343591c
Pull Request #367: Update for compatibility with PHPCS 4.0

134 of 146 new or added lines in 8 files covered. (91.78%)

1 existing line in 1 file now uncovered.

3438 of 3455 relevant lines covered (99.51%)

5.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.37
/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php
1
<?php
2
/**
3
 * PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
4
 *
5
 * @package   PHPCSExtra
6
 * @copyright 2023 PHPCSExtra Contributors
7
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
8
 * @link      https://github.com/PHPCSStandards/PHPCSExtra
9
 */
10

11
namespace PHPCSExtra\Universal\Sniffs\UseStatements;
12

13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Sniffs\Sniff;
15
use PHP_CodeSniffer\Util\Tokens;
16
use PHPCSUtils\Tokens\Collections;
17
use PHPCSUtils\Utils\NamingConventions;
18
use PHPCSUtils\Utils\UseStatements;
19

20
/**
21
 * Detects useless aliases for import use statements.
22
 *
23
 * Aliasing something to the same name as the original construct is considered useless.
24
 * Note: as OO and function names in PHP are case-insensitive, aliasing to the same name,
25
 * using a different case is also considered useless.
26
 *
27
 * @since 1.1.0
28
 */
29
final class NoUselessAliasesSniff implements Sniff
30
{
31

32
    /**
33
     * Returns an array of tokens this test wants to listen for.
34
     *
35
     * @since 1.1.0
36
     *
37
     * @return array<int|string>
38
     */
39
    public function register()
6✔
40
    {
41
        return [\T_USE];
6✔
42
    }
43

44
    /**
45
     * Processes this test, when one of its tokens is encountered.
46
     *
47
     * @since 1.1.0
48
     *
49
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
50
     * @param int                         $stackPtr  The position of the current token
51
     *                                               in the stack passed in $tokens.
52
     *
53
     * @return void
54
     */
55
    public function process(File $phpcsFile, $stackPtr)
6✔
56
    {
57
        if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) {
6✔
58
            // Closure or trait use statement. Bow out.
59
            return;
6✔
60
        }
61

62
        $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1));
6✔
63
        if ($endOfStatement === false) {
6✔
64
            // Parse error or live coding.
65
            return;
6✔
66
        }
67

68
        $hasAliases = $phpcsFile->findNext(\T_AS, ($stackPtr + 1), $endOfStatement);
6✔
69
        if ($hasAliases === false) {
6✔
70
            // This use import statement does not alias anything, bow out.
71
            return;
6✔
72
        }
73

74
        $useStatements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr);
6✔
75
        if (\count($useStatements, \COUNT_RECURSIVE) <= 3) {
6✔
76
            // No statements found. Shouldn't be possible, but still. Bow out.
77
            return;
6✔
78
        }
79

80
        $tokens = $phpcsFile->getTokens();
6✔
81

82
        // Collect all places where aliases are used in this use statement.
83
        $aliasPtrs = [];
6✔
84
        $currentAs = $hasAliases;
6✔
85
        do {
86
            $aliasPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($currentAs + 1), null, true);
6✔
87
            if ($aliasPtr !== false && $tokens[$aliasPtr]['code'] === \T_STRING) {
6✔
88
                $aliasPtrs[$currentAs] = $aliasPtr;
6✔
89
            }
2✔
90

91
            $currentAs = $phpcsFile->findNext(\T_AS, ($currentAs + 1), $endOfStatement);
6✔
92
        } while ($currentAs !== false);
6✔
93

94
        // Now check the names in each use statement for useless aliases.
95
        foreach ($useStatements as $type => $statements) {
6✔
96
            foreach ($statements as $alias => $qualifiedName) {
6✔
97
                $unqualifiedName = \ltrim(\substr($qualifiedName, (int) \strrpos($qualifiedName, '\\')), '\\');
6✔
98

99
                $uselessAlias = false;
6✔
100
                if ($type === 'const') {
6✔
101
                    // Do a case-sensitive comparison for constants.
102
                    if ($unqualifiedName === $alias) {
6✔
103
                        $uselessAlias = true;
6✔
104
                    }
2✔
105
                } elseif (NamingConventions::isEqual($unqualifiedName, $alias)) {
6✔
106
                    $uselessAlias = true;
6✔
107
                }
2✔
108

109
                if ($uselessAlias === false) {
6✔
110
                    continue;
6✔
111
                }
112

113
                // Now check if this is actually used as an alias or just the actual name.
114
                foreach ($aliasPtrs as $asPtr => $aliasPtr) {
6✔
115
                    if ($tokens[$aliasPtr]['content'] !== $alias) {
6✔
116
                        continue;
6✔
117
                    }
118

119
                    // Make sure this is really the right one.
120
                    $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($asPtr - 1), null, true);
6✔
121
                    if (isset(Collections::nameTokens()[$tokens[$prev]['code']]) === false) {
6✔
NEW
122
                        continue;
×
123
                    } elseif ($tokens[$prev]['code'] === \T_STRING
6✔
124
                        && $tokens[$prev]['content'] !== $unqualifiedName
6✔
125
                    ) {
2✔
126
                        continue;
4✔
127
                    } elseif ($tokens[$prev]['code'] === \T_NAME_QUALIFIED
6✔
128
                        && $tokens[$prev]['content'] !== $qualifiedName
6✔
129
                        && \substr($qualifiedName, -(\strlen($tokens[$prev]['content']))) !== $tokens[$prev]['content']
6✔
130
                    ) {
2✔
131
                        continue;
2✔
132
                    } elseif ($tokens[$prev]['code'] === \T_NAME_FULLY_QUALIFIED
6✔
133
                        && $tokens[$prev]['content'] !== '\\' . $qualifiedName
6✔
134
                        && \substr($qualifiedName, -(\strlen($tokens[$prev]['content']))) !== $tokens[$prev]['content']
6✔
135
                    ) {
2✔
UNCOV
136
                        continue;
×
137
                    }
138

139
                    $error = 'Useless alias "%s" found for import of "%s"';
6✔
140
                    $code  = 'Found';
6✔
141
                    $data  = [$alias, $qualifiedName];
6✔
142

143
                    // Okay, so this is the one which should be flagged.
144
                    $hasComments = $phpcsFile->findNext(Tokens::$commentTokens, ($prev + 1), $aliasPtr);
6✔
145
                    if ($hasComments !== false) {
6✔
146
                        // Don't auto-fix if there are comments.
147
                        $phpcsFile->addError($error, $aliasPtr, $code, $data);
6✔
148
                        break;
6✔
149
                    }
150

151
                    $fix = $phpcsFile->addFixableError($error, $aliasPtr, $code, $data);
6✔
152

153
                    if ($fix === true) {
6✔
154
                        $phpcsFile->fixer->beginChangeset();
6✔
155

156
                        for ($i = ($prev + 1); $i <= $aliasPtr; $i++) {
6✔
157
                            $phpcsFile->fixer->replaceToken($i, '');
6✔
158
                        }
2✔
159

160
                        $phpcsFile->fixer->endChangeset();
6✔
161
                    }
2✔
162

163
                    break;
6✔
164
                }
2✔
165
            }
2✔
166
        }
2✔
167
    }
3✔
168
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc