1<?php 2/** 3 * Verifies that control statements conform to their coding standards. 4 * 5 * PHP version 5 6 * 7 * @category PHP 8 * @package PHP_CodeSniffer 9 * @author Greg Sherwood <gsherwood@squiz.net> 10 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 11 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 12 * @link http://pear.php.net/package/PHP_CodeSniffer 13 */ 14 15/** 16 * Verifies that control statements conform to their coding standards. 17 * 18 * @category PHP 19 * @package PHP_CodeSniffer 20 * @author Greg Sherwood <gsherwood@squiz.net> 21 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 22 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 23 * @version Release: @package_version@ 24 * @link http://pear.php.net/package/PHP_CodeSniffer 25 */ 26class Squiz_Sniffs_ControlStructures_ControlSignatureSniff implements PHP_CodeSniffer_Sniff 27{ 28 29 /** 30 * A list of tokenizers this sniff supports. 31 * 32 * @var array 33 */ 34 public $supportedTokenizers = array( 35 'PHP', 36 'JS', 37 ); 38 39 40 /** 41 * Returns an array of tokens this test wants to listen for. 42 * 43 * @return int[] 44 */ 45 public function register() 46 { 47 return array( 48 T_TRY, 49 T_CATCH, 50 T_DO, 51 T_WHILE, 52 T_FOR, 53 T_IF, 54 T_FOREACH, 55 T_ELSE, 56 T_ELSEIF, 57 T_SWITCH, 58 ); 59 60 }//end register() 61 62 63 /** 64 * Processes this test, when one of its tokens is encountered. 65 * 66 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 67 * @param int $stackPtr The position of the current token in the 68 * stack passed in $tokens. 69 * 70 * @return void 71 */ 72 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 73 { 74 $tokens = $phpcsFile->getTokens(); 75 76 if (isset($tokens[($stackPtr + 1)]) === false) { 77 return; 78 } 79 80 // Single space after the keyword. 81 $found = 1; 82 if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { 83 $found = 0; 84 } else if ($tokens[($stackPtr + 1)]['content'] !== ' ') { 85 if (strpos($tokens[($stackPtr + 1)]['content'], $phpcsFile->eolChar) !== false) { 86 $found = 'newline'; 87 } else { 88 $found = strlen($tokens[($stackPtr + 1)]['content']); 89 } 90 } 91 92 if ($found !== 1) { 93 $error = 'Expected 1 space after %s keyword; %s found'; 94 $data = array( 95 strtoupper($tokens[$stackPtr]['content']), 96 $found, 97 ); 98 99 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword', $data); 100 if ($fix === true) { 101 if ($found === 0) { 102 $phpcsFile->fixer->addContent($stackPtr, ' '); 103 } else { 104 $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' '); 105 } 106 } 107 } 108 109 // Single space after closing parenthesis. 110 if (isset($tokens[$stackPtr]['parenthesis_closer']) === true 111 && isset($tokens[$stackPtr]['scope_opener']) === true 112 ) { 113 $closer = $tokens[$stackPtr]['parenthesis_closer']; 114 $opener = $tokens[$stackPtr]['scope_opener']; 115 $content = $phpcsFile->getTokensAsString(($closer + 1), ($opener - $closer - 1)); 116 117 if ($content !== ' ') { 118 $error = 'Expected 1 space after closing parenthesis; found %s'; 119 if (trim($content) === '') { 120 $found = strlen($content); 121 } else { 122 $found = '"'.str_replace($phpcsFile->eolChar, '\n', $content).'"'; 123 } 124 125 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseParenthesis', array($found)); 126 if ($fix === true) { 127 if ($closer === ($opener - 1)) { 128 $phpcsFile->fixer->addContent($closer, ' '); 129 } else { 130 $phpcsFile->fixer->beginChangeset(); 131 if (trim($content) === '') { 132 $phpcsFile->fixer->addContent($closer, ' '); 133 if ($found !== 0) { 134 for ($i = ($closer + 1); $i < $opener; $i++) { 135 $phpcsFile->fixer->replaceToken($i, ''); 136 } 137 } 138 } else { 139 $phpcsFile->fixer->addContent($closer, ' '.$tokens[$opener]['content']); 140 $phpcsFile->fixer->replaceToken($opener, ''); 141 142 if ($tokens[$opener]['line'] !== $tokens[$closer]['line']) { 143 $next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true); 144 if ($tokens[$next]['line'] !== $tokens[$opener]['line']) { 145 for ($i = ($opener + 1); $i < $next; $i++) { 146 $phpcsFile->fixer->replaceToken($i, ''); 147 } 148 } 149 } 150 } 151 152 $phpcsFile->fixer->endChangeset(); 153 }//end if 154 }//end if 155 }//end if 156 }//end if 157 158 // Single newline after opening brace. 159 if (isset($tokens[$stackPtr]['scope_opener']) === true) { 160 $opener = $tokens[$stackPtr]['scope_opener']; 161 for ($next = ($opener + 1); $next < $phpcsFile->numTokens; $next++) { 162 $code = $tokens[$next]['code']; 163 164 if ($code === T_WHITESPACE 165 || ($code === T_INLINE_HTML 166 && trim($tokens[$next]['content']) === '') 167 ) { 168 continue; 169 } 170 171 // Skip all empty tokens on the same line as the opener. 172 if ($tokens[$next]['line'] === $tokens[$opener]['line'] 173 && (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === true 174 || $code === T_CLOSE_TAG) 175 ) { 176 continue; 177 } 178 179 // We found the first bit of a code, or a comment on the 180 // following line. 181 break; 182 }//end for 183 184 if ($tokens[$next]['line'] === $tokens[$opener]['line']) { 185 $error = 'Newline required after opening brace'; 186 $fix = $phpcsFile->addFixableError($error, $opener, 'NewlineAfterOpenBrace'); 187 if ($fix === true) { 188 $phpcsFile->fixer->beginChangeset(); 189 for ($i = ($opener + 1); $i < $next; $i++) { 190 if (trim($tokens[$i]['content']) !== '') { 191 break; 192 } 193 194 // Remove whitespace. 195 $phpcsFile->fixer->replaceToken($i, ''); 196 } 197 198 $phpcsFile->fixer->addContent($opener, $phpcsFile->eolChar); 199 $phpcsFile->fixer->endChangeset(); 200 } 201 }//end if 202 } else if ($tokens[$stackPtr]['code'] === T_WHILE) { 203 // Zero spaces after parenthesis closer. 204 $closer = $tokens[$stackPtr]['parenthesis_closer']; 205 $found = 0; 206 if ($tokens[($closer + 1)]['code'] === T_WHITESPACE) { 207 if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) { 208 $found = 'newline'; 209 } else { 210 $found = strlen($tokens[($closer + 1)]['content']); 211 } 212 } 213 214 if ($found !== 0) { 215 $error = 'Expected 0 spaces before semicolon; %s found'; 216 $data = array($found); 217 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceBeforeSemicolon', $data); 218 if ($fix === true) { 219 $phpcsFile->fixer->replaceToken(($closer + 1), ''); 220 } 221 } 222 }//end if 223 224 // Only want to check multi-keyword structures from here on. 225 if ($tokens[$stackPtr]['code'] === T_DO) { 226 if (isset($tokens[$stackPtr]['scope_closer']) === false) { 227 return; 228 } 229 230 $closer = $tokens[$stackPtr]['scope_closer']; 231 } else if ($tokens[$stackPtr]['code'] === T_ELSE 232 || $tokens[$stackPtr]['code'] === T_ELSEIF 233 || $tokens[$stackPtr]['code'] === T_CATCH 234 ) { 235 if (isset($tokens[$stackPtr]['scope_opener']) === true 236 && $tokens[$tokens[$stackPtr]['scope_opener']]['code'] === T_COLON 237 ) { 238 // Special case for alternate syntax, where this token is actually 239 // the closer for the previous block, so there is no spacing to check. 240 return; 241 } 242 243 $closer = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true); 244 if ($closer === false || $tokens[$closer]['code'] !== T_CLOSE_CURLY_BRACKET) { 245 return; 246 } 247 } else { 248 return; 249 }//end if 250 251 // Single space after closing brace. 252 $found = 1; 253 if ($tokens[($closer + 1)]['code'] !== T_WHITESPACE) { 254 $found = 0; 255 } else if ($tokens[($closer + 1)]['content'] !== ' ') { 256 if (strpos($tokens[($closer + 1)]['content'], $phpcsFile->eolChar) !== false) { 257 $found = 'newline'; 258 } else { 259 $found = strlen($tokens[($closer + 1)]['content']); 260 } 261 } 262 263 if ($found !== 1) { 264 $error = 'Expected 1 space after closing brace; %s found'; 265 $data = array($found); 266 $fix = $phpcsFile->addFixableError($error, $closer, 'SpaceAfterCloseBrace', $data); 267 if ($fix === true) { 268 if ($found === 0) { 269 $phpcsFile->fixer->addContent($closer, ' '); 270 } else { 271 $phpcsFile->fixer->replaceToken(($closer + 1), ' '); 272 } 273 } 274 } 275 276 }//end process() 277 278 279}//end class 280