Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 40 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
UnusedGlobalVariablesSniff | |
0.00% |
0 / 40 |
|
0.00% |
0 / 2 |
420 | |
0.00% |
0 / 1 |
register | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
process | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
380 |
1 | <?php |
2 | /** |
3 | * Detect unused MediaWiki global variable. |
4 | * Unused global variables should be removed. |
5 | */ |
6 | |
7 | namespace MediaWiki\Sniffs\VariableAnalysis; |
8 | |
9 | use PHP_CodeSniffer\Files\File; |
10 | use PHP_CodeSniffer\Sniffs\Sniff; |
11 | |
12 | class UnusedGlobalVariablesSniff implements Sniff { |
13 | |
14 | /** |
15 | * @inheritDoc |
16 | */ |
17 | public function register(): array { |
18 | return [ T_FUNCTION, T_CLOSURE ]; |
19 | } |
20 | |
21 | /** |
22 | * @param File $phpcsFile |
23 | * @param int $stackPtr The current token index. |
24 | * @return void |
25 | */ |
26 | public function process( File $phpcsFile, $stackPtr ) { |
27 | $tokens = $phpcsFile->getTokens(); |
28 | if ( !isset( $tokens[$stackPtr]['scope_opener'] ) ) { |
29 | // An interface or abstract function which doesn't have a body |
30 | return; |
31 | } |
32 | |
33 | $scopeOpener = $tokens[$stackPtr]['scope_opener'] + 1; |
34 | $scopeCloser = $tokens[$stackPtr]['scope_closer']; |
35 | |
36 | $endOfGlobal = 0; |
37 | $globalVariables = []; |
38 | $matches = []; |
39 | $delayedSkip = []; |
40 | |
41 | for ( $i = $scopeOpener; $i < $scopeCloser; $i++ ) { |
42 | // Process a delayed skip |
43 | if ( isset( $delayedSkip[$i] ) ) { |
44 | $i = $delayedSkip[$i]; |
45 | continue; |
46 | } |
47 | $code = $tokens[$i]['code']; |
48 | if ( ( $code === T_CLOSURE || $code === T_FUNCTION || $code === T_ANON_CLASS ) |
49 | && isset( $tokens[$i]['scope_closer'] ) |
50 | ) { |
51 | if ( $code === T_CLOSURE && isset( $tokens[$i]['parenthesis_closer'] ) ) { |
52 | // Cannot skip directly to the end of closure |
53 | // The use statement needs to be processed |
54 | $delayedSkip[$tokens[$i]['scope_opener']] = $tokens[$i]['scope_closer']; |
55 | |
56 | // Skip the argument list of the closure |
57 | $i = $tokens[$i]['parenthesis_closer']; |
58 | } else { |
59 | // Skip to the end of the inner function/anon class and continue |
60 | $i = $tokens[$i]['scope_closer']; |
61 | } |
62 | continue; |
63 | } |
64 | |
65 | if ( $code === T_GLOBAL ) { |
66 | $endOfGlobal = $phpcsFile->findEndOfStatement( $i, T_COMMA ); |
67 | } elseif ( $code === T_VARIABLE && $tokens[$i - 1]['code'] !== T_DOLLAR ) { |
68 | // Note, this skips dynamic variable names. |
69 | $variableName = $tokens[$i]['content']; |
70 | if ( $i < $endOfGlobal ) { |
71 | $globalVariables[$variableName] = $i; |
72 | } else { |
73 | unset( $globalVariables[$variableName] ); |
74 | } |
75 | } elseif ( ( $code === T_DOUBLE_QUOTED_STRING || $code === T_HEREDOC ) |
76 | // Avoid the regex below when there are no globals to look for anyway |
77 | && $globalVariables |
78 | ) { |
79 | preg_match_all( '/\${?(\w+)/', $tokens[$i]['content'], $matches ); |
80 | foreach ( $matches[1] as $variableName ) { |
81 | unset( $globalVariables[ '$' . $variableName ] ); |
82 | } |
83 | } |
84 | } |
85 | |
86 | foreach ( $globalVariables as $variableName => $variableIndex ) { |
87 | $phpcsFile->addWarning( |
88 | 'Global %s is never used.', |
89 | $variableIndex, |
90 | 'UnusedGlobal' . $variableName, |
91 | [ $variableName ] |
92 | ); |
93 | } |
94 | } |
95 | } |