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