1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of Zenify |
5
|
|
|
* Copyright (c) 2012 Tomas Votruba (http://tomasvotruba.cz) |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace ZenifyCodingStandard\Sniffs\WhiteSpace; |
9
|
|
|
|
10
|
|
|
use PHP_CodeSniffer_File; |
11
|
|
|
use PHP_CodeSniffer_Sniff; |
12
|
|
|
use PHP_CodeSniffer_Tokens; |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Rules: |
17
|
|
|
* - Operator should be surrounded by spaces or on new line. |
18
|
|
|
|
19
|
|
|
* Exceptions: |
20
|
|
|
* - Function's defaults, ?:, +=, &$var and similar. |
21
|
|
|
*/ |
22
|
|
|
final class OperatorSpacingSniff implements PHP_CodeSniffer_Sniff |
23
|
|
|
{ |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var int |
27
|
|
|
*/ |
28
|
|
|
private $position; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var array |
32
|
|
|
*/ |
33
|
|
|
private $token; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var array |
37
|
|
|
*/ |
38
|
|
|
private $tokens; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var PHP_CodeSniffer_File |
42
|
|
|
*/ |
43
|
|
|
private $file; |
44
|
|
|
|
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* {@inheritdoc} |
48
|
|
|
*/ |
49
|
1 |
|
public function register() |
50
|
|
|
{ |
51
|
1 |
|
$tokens = array_merge( |
52
|
|
|
PHP_CodeSniffer_Tokens::$booleanOperators, |
53
|
|
|
PHP_CodeSniffer_Tokens::$comparisonTokens, |
54
|
|
|
PHP_CodeSniffer_Tokens::$operators, |
55
|
|
|
PHP_CodeSniffer_Tokens::$assignmentTokens, |
56
|
1 |
|
[T_INLINE_THEN, T_INLINE_ELSE] |
57
|
|
|
); |
58
|
|
|
|
59
|
1 |
|
return $tokens; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* {@inheritdoc} |
65
|
|
|
*/ |
66
|
1 |
|
public function process(PHP_CodeSniffer_File $file, $position) |
67
|
|
|
{ |
68
|
1 |
|
$this->file = $file; |
69
|
1 |
|
$this->position = $position; |
70
|
1 |
|
$this->tokens = $file->getTokens(); |
71
|
1 |
|
$this->token = $this->tokens[$position]; |
72
|
|
|
|
73
|
1 |
|
if ($this->isToBeSkipped()) { |
74
|
1 |
|
return; |
75
|
|
|
} |
76
|
|
|
|
77
|
1 |
|
$isSpaceBefore = ($this->tokens[$position - 1]['code'] === T_WHITESPACE); |
78
|
1 |
|
$isSpaceAfter = ($this->tokens[$position + 1]['code'] === T_WHITESPACE); |
79
|
1 |
|
$isNewlineAfter = ($this->tokens[$position]['line'] !== $this->tokens[$position + 2]['line']); |
80
|
|
|
|
81
|
1 |
|
if ( ! $isSpaceBefore || ! ($isSpaceAfter || $isNewlineAfter) ) { |
82
|
1 |
|
$error = 'Operator "%s" should be surrounded by spaces or on new line.'; |
83
|
|
|
$data = [ |
84
|
1 |
|
$this->tokens[$position]['content'] |
85
|
|
|
]; |
86
|
1 |
|
$file->addError($error, $position, '', $data); |
87
|
|
|
} |
88
|
1 |
|
} |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @return bool |
93
|
|
|
*/ |
94
|
1 |
|
private function isToBeSkipped() |
95
|
|
|
{ |
96
|
1 |
|
if ($this->isDefaultValueInFunctionDeclaration()) { |
97
|
|
|
return TRUE; |
98
|
|
|
} |
99
|
|
|
|
100
|
1 |
|
if ($this->isShortTernaryOperator()) { |
101
|
1 |
|
return TRUE; |
102
|
|
|
} |
103
|
|
|
|
104
|
1 |
|
if ($this->isMinusAssign()) { |
105
|
|
|
return TRUE; |
106
|
|
|
} |
107
|
|
|
|
108
|
1 |
|
if ($this->isReference()) { |
109
|
1 |
|
return TRUE; |
110
|
|
|
} |
111
|
|
|
|
112
|
1 |
|
return FALSE; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @return bool |
118
|
|
|
*/ |
119
|
1 |
|
private function isDefaultValueInFunctionDeclaration() |
120
|
|
|
{ |
121
|
1 |
|
if ($this->isTokenStartOfDefaultValue()) { |
122
|
|
|
$parenthesis = array_keys($this->token['nested_parenthesis']); |
123
|
|
|
$bracket = array_pop($parenthesis); |
124
|
|
|
if (isset($this->tokens[$bracket]['parenthesis_owner']) === TRUE) { |
125
|
|
|
$function = $this->tokens[$bracket]['parenthesis_owner']; |
126
|
|
|
if ($this->tokens[$function]['code'] === T_FUNCTION || $this->tokens[$function]['code'] === T_CLOSURE) { |
127
|
|
|
return TRUE; |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
1 |
|
return FALSE; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @return bool |
138
|
|
|
*/ |
139
|
1 |
|
private function isTokenStartOfDefaultValue() |
140
|
|
|
{ |
141
|
1 |
|
if ($this->token['code'] !== T_EQUAL && $this->token['code'] !== T_MINUS) { |
142
|
1 |
|
return FALSE; |
143
|
|
|
} |
144
|
|
|
|
145
|
1 |
|
if (isset($this->token['nested_parenthesis'])) { |
146
|
|
|
return TRUE; |
147
|
|
|
} |
148
|
|
|
|
149
|
1 |
|
return FALSE; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @return bool |
155
|
|
|
*/ |
156
|
1 |
|
private function isShortTernaryOperator() |
157
|
|
|
{ |
158
|
|
|
// *?*: |
159
|
1 |
View Code Duplication |
if ($this->token['code'] === T_INLINE_THEN && $this->tokens[$this->position + 1]['code'] === T_INLINE_ELSE) { |
|
|
|
|
160
|
1 |
|
return TRUE; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// ?*:* |
164
|
1 |
View Code Duplication |
if ($this->tokens[$this->position - 1]['code'] === T_INLINE_THEN && $this->token['code'] === T_INLINE_ELSE) { |
|
|
|
|
165
|
1 |
|
return TRUE; |
166
|
|
|
} |
167
|
|
|
|
168
|
1 |
|
return FALSE; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @return bool |
174
|
|
|
*/ |
175
|
1 |
|
private function isMinusAssign() |
176
|
|
|
{ |
177
|
1 |
|
if ($this->tokens[$this->position]['code'] === T_MINUS) { |
178
|
|
|
// Check minus spacing, but make sure we aren't just assigning |
179
|
|
|
// a minus value or returning one. |
180
|
|
|
$prev = $this->file->findPrevious(T_WHITESPACE, ($this->position - 1), NULL, TRUE); |
181
|
|
|
if ($this->tokens[$prev]['code'] === T_RETURN) { |
182
|
|
|
// Just returning a negative value; eg. (return -1). |
183
|
|
|
return TRUE; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
if (isset(PHP_CodeSniffer_Tokens::$operators[$this->tokens[$prev]['code']]) === TRUE) { |
187
|
|
|
// Just trying to operate on a negative value; eg. ($var * -1). |
188
|
|
|
return TRUE; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
if (isset(PHP_CodeSniffer_Tokens::$comparisonTokens[$this->tokens[$prev]['code']]) === TRUE) { |
192
|
|
|
// Just trying to compare a negative value; eg. ($var === -1). |
193
|
|
|
return TRUE; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$this->tokens[$prev]['code']]) === TRUE) { |
197
|
|
|
// Just trying to assign a negative value; eg. ($var = -1). |
198
|
|
|
return TRUE; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// A list of tokens that indicate that the token is not |
202
|
|
|
// part of an arithmetic operation. |
203
|
|
|
$invalidTokens = [ |
204
|
|
|
T_COMMA => TRUE, |
205
|
|
|
T_OPEN_PARENTHESIS => TRUE, |
206
|
|
|
T_OPEN_SQUARE_BRACKET => TRUE, |
207
|
|
|
T_DOUBLE_ARROW => TRUE, |
208
|
|
|
T_COLON => TRUE, |
209
|
|
|
T_INLINE_THEN => TRUE, |
210
|
|
|
T_INLINE_ELSE => TRUE, |
211
|
|
|
T_CASE => TRUE |
212
|
|
|
]; |
213
|
|
|
|
214
|
|
|
if (isset($invalidTokens[$this->tokens[$prev]['code']]) === TRUE) { |
215
|
|
|
// Just trying to use a negative value; eg. myFunction($var, -2). |
216
|
|
|
return TRUE; |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
1 |
|
return FALSE; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* @return bool |
226
|
|
|
*/ |
227
|
1 |
|
private function isReference() |
228
|
|
|
{ |
229
|
1 |
|
if ($this->tokens[$this->position]['code'] === T_BITWISE_AND && $this->file->isReference($this->position)) { |
230
|
1 |
|
return TRUE; |
231
|
|
|
} |
232
|
|
|
|
233
|
1 |
|
return FALSE; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
} |
237
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.