1<?php 2/** 3 * Checks that the opening brace of a class/interface/trait is on the same line as the class declaration. 4 * 5 * @author Greg Sherwood <gsherwood@squiz.net> 6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) 7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 8 */ 9 10namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Classes; 11 12use PHP_CodeSniffer\Files\File; 13use PHP_CodeSniffer\Sniffs\Sniff; 14 15class OpeningBraceSameLineSniff implements Sniff 16{ 17 18 19 /** 20 * Returns an array of tokens this test wants to listen for. 21 * 22 * @return array 23 */ 24 public function register() 25 { 26 return [ 27 T_CLASS, 28 T_INTERFACE, 29 T_TRAIT, 30 ]; 31 32 }//end register() 33 34 35 /** 36 * Processes this test, when one of its tokens is encountered. 37 * 38 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. 39 * @param int $stackPtr The position of the current token 40 * in the stack passed in $tokens. 41 * 42 * @return void 43 */ 44 public function process(File $phpcsFile, $stackPtr) 45 { 46 $tokens = $phpcsFile->getTokens(); 47 $scopeIdentifier = $phpcsFile->findNext(T_STRING, ($stackPtr + 1)); 48 $errorData = [strtolower($tokens[$stackPtr]['content']).' '.$tokens[$scopeIdentifier]['content']]; 49 50 if (isset($tokens[$stackPtr]['scope_opener']) === false) { 51 $error = 'Possible parse error: %s missing opening or closing brace'; 52 $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData); 53 return; 54 } 55 56 $openingBrace = $tokens[$stackPtr]['scope_opener']; 57 58 // Is the brace on the same line as the class/interface/trait declaration ? 59 $lastClassLineToken = $phpcsFile->findPrevious(T_WHITESPACE, ($openingBrace - 1), $stackPtr, true); 60 $lastClassLine = $tokens[$lastClassLineToken]['line']; 61 $braceLine = $tokens[$openingBrace]['line']; 62 $lineDifference = ($braceLine - $lastClassLine); 63 64 if ($lineDifference > 0) { 65 $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line'); 66 $error = 'Opening brace should be on the same line as the declaration for %s'; 67 $fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine', $errorData); 68 if ($fix === true) { 69 $phpcsFile->fixer->beginChangeset(); 70 $phpcsFile->fixer->addContent($lastClassLineToken, ' {'); 71 $phpcsFile->fixer->replaceToken($openingBrace, ''); 72 $phpcsFile->fixer->endChangeset(); 73 } 74 } else { 75 $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line'); 76 } 77 78 // Is the opening brace the last thing on the line ? 79 $next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true); 80 if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) { 81 if ($next === $tokens[$stackPtr]['scope_closer']) { 82 // Ignore empty classes. 83 return; 84 } 85 86 $error = 'Opening brace must be the last content on the line'; 87 $fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace'); 88 if ($fix === true) { 89 $phpcsFile->fixer->addNewline($openingBrace); 90 } 91 } 92 93 // Only continue checking if the opening brace looks good. 94 if ($lineDifference > 0) { 95 return; 96 } 97 98 // Is there precisely one space before the opening brace ? 99 if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) { 100 $length = 0; 101 } else if ($tokens[($openingBrace - 1)]['content'] === "\t") { 102 $length = '\t'; 103 } else { 104 $length = $tokens[($openingBrace - 1)]['length']; 105 } 106 107 if ($length !== 1) { 108 $error = 'Expected 1 space before opening brace; found %s'; 109 $data = [$length]; 110 $fix = $phpcsFile->addFixableError($error, $openingBrace, 'SpaceBeforeBrace', $data); 111 if ($fix === true) { 112 if ($length === 0 || $length === '\t') { 113 $phpcsFile->fixer->addContentBefore($openingBrace, ' '); 114 } else { 115 $phpcsFile->fixer->replaceToken(($openingBrace - 1), ' '); 116 } 117 } 118 } 119 120 }//end process() 121 122 123}//end class 124