1
|
|
|
<?php declare(strict_types = 1); |
2
|
|
|
|
3
|
|
|
namespace Codor\Sniffs\Classes; |
4
|
|
|
|
5
|
|
|
use PHP_CodeSniffer_Sniff; |
6
|
|
|
use PHP_CodeSniffer_File; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* @SuppressWarnings(PHPMD.LongVariable) |
10
|
|
|
*/ |
11
|
|
|
class FinalPrivateSniff implements PHP_CodeSniffer_Sniff |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* Returns the token types that this sniff is interested in. |
15
|
|
|
* @return array |
16
|
|
|
*/ |
17
|
|
|
public function register(): array |
18
|
|
|
{ |
19
|
|
|
return [T_CLASS]; |
20
|
|
|
} |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Is the class marked as final? |
24
|
|
|
* @var boolean |
25
|
|
|
*/ |
26
|
|
|
protected $classIsMarkedFinal = false; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* List of protected methods found. |
30
|
|
|
* @var array |
31
|
|
|
*/ |
32
|
|
|
protected $protectedMethodTokens = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* List of protected variables found. |
36
|
|
|
* @var array |
37
|
|
|
*/ |
38
|
|
|
protected $protectedVariableTokens = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Processes the tokens that this sniff is interested in. |
42
|
|
|
* |
43
|
|
|
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. |
44
|
|
|
* @param int $stackPtr The position in the stack where |
45
|
|
|
* the token was found. |
46
|
|
|
* @return void |
47
|
|
|
* @SuppressWarnings(PHPMD.UnusedLocalVariable) |
48
|
|
|
*/ |
49
|
|
|
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) |
50
|
|
|
{ |
51
|
|
|
$this->classIsMarkedFinal = false; |
52
|
|
|
$this->protectedMethodTokens = []; |
53
|
|
|
$this->protectedVariableTokens = []; |
54
|
|
|
|
55
|
|
|
$tokens = $phpcsFile->getTokens(); |
56
|
|
|
|
57
|
|
|
foreach ($tokens as $index => $token) { |
58
|
|
|
$this->handleToken($tokens, $index); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
$this->handleErrors($phpcsFile, $stackPtr); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Handle the incoming token. |
66
|
|
|
* @param array $tokens List of tokens. |
67
|
|
|
* @param integer $index Current token index. |
68
|
|
|
* @return void |
69
|
|
|
*/ |
70
|
|
|
protected function handleToken($tokens, $index) |
71
|
|
|
{ |
72
|
|
|
$tokenType = $tokens[$index]['type']; |
73
|
|
|
|
74
|
|
|
if ($tokenType === 'T_FINAL') { |
75
|
|
|
$this->classIsMarkedFinal = true; |
76
|
|
|
return; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
if (! $this->classIsMarkedFinal) { |
80
|
|
|
return; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
if ($tokenType !== 'T_PROTECTED') { |
84
|
|
|
return; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
$this->handleFoundProtectedElement($tokens, $index); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Handles found protected method or variable within |
92
|
|
|
* a final class. |
93
|
|
|
* @param array $tokens List of tokens. |
94
|
|
|
* @param integer $index Current token index. |
95
|
|
|
* @return void |
96
|
|
|
*/ |
97
|
|
|
protected function handleFoundProtectedElement($tokens, $index) |
98
|
|
|
{ |
99
|
|
|
$type = $tokens[$index+2]['type']; |
100
|
|
|
|
101
|
|
|
if ($type === 'T_VARIABLE') { |
102
|
|
|
$this->protectedVariableTokens[] = $tokens[$index+2]; |
103
|
|
|
return; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
$this->protectedMethodTokens[] = $tokens[$index+4]; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Handle any errors. |
111
|
|
|
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. |
112
|
|
|
* @param int $stackPtr The position in the stack where. |
113
|
|
|
* @return void |
114
|
|
|
*/ |
115
|
|
|
protected function handleErrors($phpcsFile, $stackPtr) |
116
|
|
|
{ |
117
|
|
|
foreach ($this->protectedMethodTokens as $protectedMethodToken) { |
118
|
|
|
$this->handleProtectedMethodToken($protectedMethodToken, $phpcsFile); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
foreach ($this->protectedVariableTokens as $protectedVariableToken) { |
122
|
|
|
$this->handleProtectedVariableToken($protectedVariableToken, $phpcsFile, $stackPtr); |
|
|
|
|
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Add a protected method found error. |
128
|
|
|
* @param array $token Token data. |
129
|
|
|
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. |
130
|
|
|
* @return void |
131
|
|
|
*/ |
132
|
|
|
protected function handleProtectedMethodToken($token, $phpcsFile) |
133
|
|
|
{ |
134
|
|
|
$methodName = $token['content']; |
135
|
|
|
$line = $token['line']; |
136
|
|
|
$phpcsFile->addError("Final Class contains a protected method {$methodName} - should be private.", $line); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Add a protected variable found error. |
141
|
|
|
* @param array $token Token data. |
142
|
|
|
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. |
143
|
|
|
* @return void |
144
|
|
|
*/ |
145
|
|
|
protected function handleProtectedVariableToken($token, $phpcsFile) |
146
|
|
|
{ |
147
|
|
|
$variableName = $token['content']; |
148
|
|
|
$line = $token['line']; |
149
|
|
|
$phpcsFile->addError("Final Class contains a protected variable {$variableName} - should be private.", $line); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.