Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * Verifies that control statements conform to their coding standards.
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace Drupal\Sniffs\ControlStructures;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Sniffs\Sniff;
|
Chris@17
|
14 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * Verifies that control statements conform to their coding standards.
|
Chris@0
|
18 *
|
Chris@17
|
19 * Largely copied from
|
Chris@17
|
20 * \PHP_CodeSniffer\Standards\Squiz\Sniffs\ControlStructures\ControlSignatureSniff
|
Chris@17
|
21 * and adapted for Drupal's else on new lines.
|
Chris@0
|
22 *
|
Chris@0
|
23 * @category PHP
|
Chris@0
|
24 * @package PHP_CodeSniffer
|
Chris@0
|
25 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
26 */
|
Chris@17
|
27 class ControlSignatureSniff implements Sniff
|
Chris@0
|
28 {
|
Chris@0
|
29
|
Chris@0
|
30 /**
|
Chris@0
|
31 * A list of tokenizers this sniff supports.
|
Chris@0
|
32 *
|
Chris@0
|
33 * @var array
|
Chris@0
|
34 */
|
Chris@0
|
35 public $supportedTokenizers = array(
|
Chris@0
|
36 'PHP',
|
Chris@0
|
37 'JS',
|
Chris@0
|
38 );
|
Chris@0
|
39
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * Returns an array of tokens this test wants to listen for.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @return int[]
|
Chris@0
|
45 */
|
Chris@0
|
46 public function register()
|
Chris@0
|
47 {
|
Chris@0
|
48 return array(
|
Chris@0
|
49 T_TRY,
|
Chris@0
|
50 T_CATCH,
|
Chris@0
|
51 T_DO,
|
Chris@0
|
52 T_WHILE,
|
Chris@0
|
53 T_FOR,
|
Chris@0
|
54 T_IF,
|
Chris@0
|
55 T_FOREACH,
|
Chris@0
|
56 T_ELSE,
|
Chris@0
|
57 T_ELSEIF,
|
Chris@0
|
58 T_SWITCH,
|
Chris@0
|
59 );
|
Chris@0
|
60
|
Chris@0
|
61 }//end register()
|
Chris@0
|
62
|
Chris@0
|
63
|
Chris@0
|
64 /**
|
Chris@0
|
65 * Processes this test, when one of its tokens is encountered.
|
Chris@0
|
66 *
|
Chris@17
|
67 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
68 * @param int $stackPtr The position of the current token in the
|
Chris@17
|
69 * stack passed in $tokens.
|
Chris@0
|
70 *
|
Chris@0
|
71 * @return void
|
Chris@0
|
72 */
|
Chris@17
|
73 public function process(File $phpcsFile, $stackPtr)
|
Chris@0
|
74 {
|
Chris@0
|
75 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
76
|
Chris@0
|
77 if (isset($tokens[($stackPtr + 1)]) === false) {
|
Chris@0
|
78 return;
|
Chris@0
|
79 }
|
Chris@0
|
80
|
Chris@0
|
81 // Single space after the keyword.
|
Chris@0
|
82 $found = 1;
|
Chris@0
|
83 if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
|
Chris@0
|
84 $found = 0;
|
Chris@0
|
85 } else if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
|
Chris@0
|
86 if (strpos($tokens[($stackPtr + 1)]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
87 $found = 'newline';
|
Chris@0
|
88 } else {
|
Chris@0
|
89 $found = strlen($tokens[($stackPtr + 1)]['content']);
|
Chris@0
|
90 }
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 if ($found !== 1) {
|
Chris@0
|
94 $error = 'Expected 1 space after %s keyword; %s found';
|
Chris@0
|
95 $data = array(
|
Chris@0
|
96 strtoupper($tokens[$stackPtr]['content']),
|
Chris@0
|
97 $found,
|
Chris@0
|
98 );
|
Chris@0
|
99
|
Chris@0
|
100 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword', $data);
|
Chris@0
|
101 if ($fix === true) {
|
Chris@0
|
102 if ($found === 0) {
|
Chris@0
|
103 $phpcsFile->fixer->addContent($stackPtr, ' ');
|
Chris@0
|
104 } else {
|
Chris@0
|
105 $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
|
Chris@0
|
106 }
|
Chris@0
|
107 }
|
Chris@0
|
108 }
|
Chris@0
|
109
|
Chris@0
|
110 // Single space after closing parenthesis.
|
Chris@0
|
111 if (isset($tokens[$stackPtr]['parenthesis_closer']) === true
|
Chris@0
|
112 && isset($tokens[$stackPtr]['scope_opener']) === true
|
Chris@0
|
113 ) {
|
Chris@0
|
114 $closer = $tokens[$stackPtr]['parenthesis_closer'];
|
Chris@0
|
115 $opener = $tokens[$stackPtr]['scope_opener'];
|
Chris@0
|
116 $content = $phpcsFile->getTokensAsString(($closer + 1), ($opener - $closer - 1));
|
Chris@0
|
117
|
Chris@0
|
118 if ($content !== ' ') {
|
Chris@0
|
119 $error = 'Expected 1 space after closing parenthesis; found %s';
|
Chris@0
|
120 if (trim($content) === '') {
|
Chris@0
|
121 $found = strlen($content);
|
Chris@0
|
122 } else {
|
Chris@0
|
123 $found = '"'.str_replace($phpcsFile->eolChar, '\n', $content).'"';
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseParenthesis', array($found));
|
Chris@0
|
127 if ($fix === true) {
|
Chris@0
|
128 if ($closer === ($opener - 1)) {
|
Chris@0
|
129 $phpcsFile->fixer->addContent($closer, ' ');
|
Chris@0
|
130 } else {
|
Chris@0
|
131 $phpcsFile->fixer->beginChangeset();
|
Chris@0
|
132 $phpcsFile->fixer->addContent($closer, ' '.$tokens[$opener]['content']);
|
Chris@0
|
133 $phpcsFile->fixer->replaceToken($opener, '');
|
Chris@0
|
134
|
Chris@0
|
135 if ($tokens[$opener]['line'] !== $tokens[$closer]['line']) {
|
Chris@0
|
136 $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
|
Chris@0
|
137 if ($tokens[$next]['line'] !== $tokens[$opener]['line']) {
|
Chris@0
|
138 for ($i = ($opener + 1); $i < $next; $i++) {
|
Chris@0
|
139 $phpcsFile->fixer->replaceToken($i, '');
|
Chris@0
|
140 }
|
Chris@0
|
141 }
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 $phpcsFile->fixer->endChangeset();
|
Chris@0
|
145 }
|
Chris@0
|
146 }
|
Chris@0
|
147 }//end if
|
Chris@0
|
148 }//end if
|
Chris@0
|
149
|
Chris@0
|
150 // Single newline after opening brace.
|
Chris@0
|
151 if (isset($tokens[$stackPtr]['scope_opener']) === true) {
|
Chris@0
|
152 $opener = $tokens[$stackPtr]['scope_opener'];
|
Chris@0
|
153 for ($next = ($opener + 1); $next < $phpcsFile->numTokens; $next++) {
|
Chris@0
|
154 $code = $tokens[$next]['code'];
|
Chris@0
|
155
|
Chris@0
|
156 if ($code === T_WHITESPACE
|
Chris@0
|
157 || ($code === T_INLINE_HTML
|
Chris@0
|
158 && trim($tokens[$next]['content']) === '')
|
Chris@0
|
159 ) {
|
Chris@0
|
160 continue;
|
Chris@0
|
161 }
|
Chris@0
|
162
|
Chris@0
|
163 // Skip all empty tokens on the same line as the opener.
|
Chris@0
|
164 if ($tokens[$next]['line'] === $tokens[$opener]['line']
|
Chris@17
|
165 && (isset(Tokens::$emptyTokens[$code]) === true
|
Chris@0
|
166 || $code === T_CLOSE_TAG)
|
Chris@0
|
167 ) {
|
Chris@0
|
168 continue;
|
Chris@0
|
169 }
|
Chris@0
|
170
|
Chris@0
|
171 // We found the first bit of a code, or a comment on the
|
Chris@0
|
172 // following line.
|
Chris@0
|
173 break;
|
Chris@0
|
174 }//end for
|
Chris@0
|
175
|
Chris@0
|
176 if ($tokens[$next]['line'] === $tokens[$opener]['line']) {
|
Chris@0
|
177 $error = 'Newline required after opening brace';
|
Chris@0
|
178 $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineAfterOpenBrace');
|
Chris@0
|
179 if ($fix === true) {
|
Chris@0
|
180 $phpcsFile->fixer->beginChangeset();
|
Chris@0
|
181 for ($i = ($opener + 1); $i < $next; $i++) {
|
Chris@0
|
182 if (trim($tokens[$i]['content']) !== '') {
|
Chris@0
|
183 break;
|
Chris@0
|
184 }
|
Chris@0
|
185
|
Chris@0
|
186 // Remove whitespace.
|
Chris@0
|
187 $phpcsFile->fixer->replaceToken($i, '');
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 $phpcsFile->fixer->addContent($opener, $phpcsFile->eolChar);
|
Chris@0
|
191 $phpcsFile->fixer->endChangeset();
|
Chris@0
|
192 }
|
Chris@0
|
193 }//end if
|
Chris@0
|
194 } else if ($tokens[$stackPtr]['code'] === T_WHILE) {
|
Chris@0
|
195 // Zero spaces after parenthesis closer.
|
Chris@0
|
196 $closer = $tokens[$stackPtr]['parenthesis_closer'];
|
Chris@0
|
197 $found = 0;
|
Chris@0
|
198 if ($tokens[($closer + 1)]['code'] === T_WHITESPACE) {
|
Chris@0
|
199 if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
200 $found = 'newline';
|
Chris@0
|
201 } else {
|
Chris@0
|
202 $found = strlen($tokens[($closer + 1)]['content']);
|
Chris@0
|
203 }
|
Chris@0
|
204 }
|
Chris@0
|
205
|
Chris@0
|
206 if ($found !== 0) {
|
Chris@0
|
207 $error = 'Expected 0 spaces before semicolon; %s found';
|
Chris@0
|
208 $data = array($found);
|
Chris@0
|
209 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceBeforeSemicolon', $data);
|
Chris@0
|
210 if ($fix === true) {
|
Chris@0
|
211 $phpcsFile->fixer->replaceToken(($closer + 1), '');
|
Chris@0
|
212 }
|
Chris@0
|
213 }
|
Chris@0
|
214 }//end if
|
Chris@0
|
215
|
Chris@0
|
216 // Only want to check multi-keyword structures from here on.
|
Chris@0
|
217 if ($tokens[$stackPtr]['code'] === T_DO) {
|
Chris@0
|
218 $closer = false;
|
Chris@0
|
219 if (isset($tokens[$stackPtr]['scope_closer']) === true) {
|
Chris@0
|
220 $closer = $tokens[$stackPtr]['scope_closer'];
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 // Do-while loops should have curly braces. This is optional in
|
Chris@0
|
224 // Javascript.
|
Chris@0
|
225 if ($closer === false && $tokens[$stackPtr]['code'] === T_DO && $phpcsFile->tokenizerType === 'JS') {
|
Chris@0
|
226 $error = 'The code block in a do-while loop should be surrounded by curly braces';
|
Chris@0
|
227 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'DoWhileCurlyBraces');
|
Chris@0
|
228 $closer = $phpcsFile->findNext(T_WHILE, $stackPtr);
|
Chris@0
|
229 if ($fix === true) {
|
Chris@0
|
230 $phpcsFile->fixer->beginChangeset();
|
Chris@0
|
231 // Append an opening curly brace followed by a newline after
|
Chris@0
|
232 // the DO.
|
Chris@0
|
233 $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
|
Chris@0
|
234 if ($next !== ($stackPtr + 1)) {
|
Chris@0
|
235 $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
|
Chris@0
|
236 }
|
Chris@0
|
237
|
Chris@0
|
238 $phpcsFile->fixer->addContent($stackPtr, ' {'.$phpcsFile->eolChar);
|
Chris@0
|
239
|
Chris@0
|
240 // Prepend a closing curly brace before the WHILE and ensure
|
Chris@0
|
241 // it is on a new line.
|
Chris@0
|
242 $prepend = $phpcsFile->eolChar;
|
Chris@0
|
243 if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) {
|
Chris@0
|
244 $prepend = '';
|
Chris@0
|
245 if ($tokens[($closer - 1)]['content'] !== $phpcsFile->eolChar) {
|
Chris@0
|
246 $phpcsFile->fixer->replaceToken(($closer - 1), $phpcsFile->eolChar);
|
Chris@0
|
247 }
|
Chris@0
|
248 }
|
Chris@0
|
249
|
Chris@0
|
250 $phpcsFile->fixer->addContentBefore($closer, $prepend.'} ');
|
Chris@0
|
251 $phpcsFile->fixer->endChangeset();
|
Chris@0
|
252 }//end if
|
Chris@0
|
253 }//end if
|
Chris@0
|
254 } else if ($tokens[$stackPtr]['code'] === T_ELSE
|
Chris@0
|
255 || $tokens[$stackPtr]['code'] === T_ELSEIF
|
Chris@0
|
256 || $tokens[$stackPtr]['code'] === T_CATCH
|
Chris@0
|
257 ) {
|
Chris@17
|
258 $closer = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
Chris@0
|
259 if ($closer === false || $tokens[$closer]['code'] !== T_CLOSE_CURLY_BRACKET) {
|
Chris@0
|
260 return;
|
Chris@0
|
261 }
|
Chris@0
|
262 } else {
|
Chris@0
|
263 return;
|
Chris@0
|
264 }//end if
|
Chris@0
|
265
|
Chris@0
|
266 if ($tokens[$stackPtr]['code'] === T_DO) {
|
Chris@0
|
267 // Single space after closing brace.
|
Chris@0
|
268 $found = 1;
|
Chris@0
|
269 if ($tokens[($closer + 1)]['code'] !== T_WHITESPACE) {
|
Chris@0
|
270 $found = 0;
|
Chris@0
|
271 } else if ($tokens[($closer + 1)]['content'] !== ' ') {
|
Chris@0
|
272 if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
273 $found = 'newline';
|
Chris@0
|
274 } else {
|
Chris@0
|
275 $found = strlen($tokens[($closer + 1)]['content']);
|
Chris@0
|
276 }
|
Chris@0
|
277 }
|
Chris@0
|
278
|
Chris@0
|
279 if ($found !== 1) {
|
Chris@0
|
280 $error = 'Expected 1 space after closing brace; %s found';
|
Chris@0
|
281 $data = array($found);
|
Chris@0
|
282 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseBrace', $data);
|
Chris@0
|
283 if ($fix === true) {
|
Chris@0
|
284 if ($found === 0) {
|
Chris@0
|
285 $phpcsFile->fixer->addContent($closer, ' ');
|
Chris@0
|
286 } else {
|
Chris@0
|
287 $phpcsFile->fixer->replaceToken(($closer + 1), ' ');
|
Chris@0
|
288 }
|
Chris@0
|
289 }
|
Chris@0
|
290 }
|
Chris@0
|
291 } else {
|
Chris@0
|
292 // New line after closing brace.
|
Chris@0
|
293 $found = 'newline';
|
Chris@0
|
294 if ($tokens[($closer + 1)]['code'] !== T_WHITESPACE) {
|
Chris@0
|
295 $found = 'none';
|
Chris@0
|
296 } else if (strpos($tokens[($closer + 1)]['content'], "\n") === false) {
|
Chris@0
|
297 $found = 'spaces';
|
Chris@0
|
298 }
|
Chris@0
|
299
|
Chris@0
|
300 if ($found !== 'newline') {
|
Chris@0
|
301 $error = 'Expected newline after closing brace';
|
Chris@0
|
302 $fix = $phpcsFile->addFixableError($error, $closer, 'NewlineAfterCloseBrace');
|
Chris@0
|
303 if ($fix === true) {
|
Chris@0
|
304 if ($found === 'none') {
|
Chris@0
|
305 $phpcsFile->fixer->addContent($closer, "\n");
|
Chris@0
|
306 } else {
|
Chris@0
|
307 $phpcsFile->fixer->replaceToken(($closer + 1), "\n");
|
Chris@0
|
308 }
|
Chris@0
|
309 }
|
Chris@0
|
310 }
|
Chris@0
|
311 }//end if
|
Chris@0
|
312
|
Chris@0
|
313 }//end process()
|
Chris@0
|
314
|
Chris@0
|
315
|
Chris@0
|
316 }//end class
|