annotate vendor/drupal/coder/coder_sniffer/Drupal/Sniffs/ControlStructures/ControlSignatureSniff.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
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