1<?php 2/** 3 * Class Declaration Test. 4 * 5 * PHP version 5 6 * 7 * @category PHP 8 * @package PHP_CodeSniffer 9 * @author Greg Sherwood <gsherwood@squiz.net> 10 * @author Marc McIntyre <mmcintyre@squiz.net> 11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 12 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 13 * @link http://pear.php.net/package/PHP_CodeSniffer 14 */ 15 16if (class_exists('PSR2_Sniffs_Classes_ClassDeclarationSniff', true) === false) { 17 $error = 'Class PSR2_Sniffs_Classes_ClassDeclarationSniff not found'; 18 throw new PHP_CodeSniffer_Exception($error); 19} 20 21/** 22 * Class Declaration Test. 23 * 24 * Checks the declaration of the class and its inheritance is correct. 25 * 26 * @category PHP 27 * @package PHP_CodeSniffer 28 * @author Greg Sherwood <gsherwood@squiz.net> 29 * @author Marc McIntyre <mmcintyre@squiz.net> 30 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 31 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 32 * @version Release: @package_version@ 33 * @link http://pear.php.net/package/PHP_CodeSniffer 34 */ 35class Squiz_Sniffs_Classes_ClassDeclarationSniff extends PSR2_Sniffs_Classes_ClassDeclarationSniff 36{ 37 38 39 /** 40 * Processes this test, when one of its tokens is encountered. 41 * 42 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 43 * @param int $stackPtr The position of the current token 44 * in the stack passed in $tokens. 45 * 46 * @return void 47 */ 48 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 49 { 50 // We want all the errors from the PSR2 standard, plus some of our own. 51 parent::process($phpcsFile, $stackPtr); 52 53 $tokens = $phpcsFile->getTokens(); 54 55 // Check that this is the only class or interface in the file. 56 $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), ($stackPtr + 1)); 57 if ($nextClass !== false) { 58 // We have another, so an error is thrown. 59 $error = 'Only one interface or class is allowed in a file'; 60 $phpcsFile->addError($error, $nextClass, 'MultipleClasses'); 61 } 62 63 }//end process() 64 65 66 /** 67 * Processes the opening section of a class declaration. 68 * 69 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 70 * @param int $stackPtr The position of the current token 71 * in the stack passed in $tokens. 72 * 73 * @return void 74 */ 75 public function processOpen(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 76 { 77 parent::processOpen($phpcsFile, $stackPtr); 78 79 $tokens = $phpcsFile->getTokens(); 80 81 if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) { 82 $prevContent = $tokens[($stackPtr - 1)]['content']; 83 if ($prevContent !== $phpcsFile->eolChar) { 84 $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar)); 85 $spaces = strlen($blankSpace); 86 87 if ($tokens[($stackPtr - 2)]['code'] !== T_ABSTRACT 88 && $tokens[($stackPtr - 2)]['code'] !== T_FINAL 89 ) { 90 if ($spaces !== 0) { 91 $type = strtolower($tokens[$stackPtr]['content']); 92 $error = 'Expected 0 spaces before %s keyword; %s found'; 93 $data = array( 94 $type, 95 $spaces, 96 ); 97 98 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeKeyword', $data); 99 if ($fix === true) { 100 $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); 101 } 102 } 103 } 104 }//end if 105 }//end if 106 107 }//end processOpen() 108 109 110 /** 111 * Processes the closing section of a class declaration. 112 * 113 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 114 * @param int $stackPtr The position of the current token 115 * in the stack passed in $tokens. 116 * 117 * @return void 118 */ 119 public function processClose(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 120 { 121 $tokens = $phpcsFile->getTokens(); 122 if (isset($tokens[$stackPtr]['scope_closer']) === false) { 123 return; 124 } 125 126 $closeBrace = $tokens[$stackPtr]['scope_closer']; 127 128 // Check that the closing brace has one blank line after it. 129 for ($nextContent = ($closeBrace + 1); $nextContent < $phpcsFile->numTokens; $nextContent++) { 130 // Ignore comments on the same lines as the brace. 131 if ($tokens[$nextContent]['line'] === $tokens[$closeBrace]['line'] 132 && ($tokens[$nextContent]['code'] === T_WHITESPACE 133 || $tokens[$nextContent]['code'] === T_COMMENT) 134 ) { 135 continue; 136 } 137 138 if ($tokens[$nextContent]['code'] !== T_WHITESPACE) { 139 break; 140 } 141 } 142 143 if ($nextContent === $phpcsFile->numTokens) { 144 // Ignore the line check as this is the very end of the file. 145 $difference = 1; 146 } else { 147 $difference = ($tokens[$nextContent]['line'] - $tokens[$closeBrace]['line'] - 1); 148 } 149 150 $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), $stackPtr, true); 151 152 if ($difference === -1 153 || $tokens[$lastContent]['line'] === $tokens[$closeBrace]['line'] 154 ) { 155 $error = 'Closing %s brace must be on a line by itself'; 156 $data = array($tokens[$stackPtr]['content']); 157 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'CloseBraceSameLine', $data); 158 if ($fix === true) { 159 if ($difference === -1) { 160 $phpcsFile->fixer->addNewlineBefore($nextContent); 161 } 162 163 if ($tokens[$lastContent]['line'] === $tokens[$closeBrace]['line']) { 164 $phpcsFile->fixer->addNewlineBefore($closeBrace); 165 } 166 } 167 } else if ($tokens[($closeBrace - 1)]['code'] === T_WHITESPACE) { 168 $prevContent = $tokens[($closeBrace - 1)]['content']; 169 if ($prevContent !== $phpcsFile->eolChar) { 170 $blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar)); 171 $spaces = strlen($blankSpace); 172 if ($spaces !== 0) { 173 if ($tokens[($closeBrace - 1)]['line'] !== $tokens[$closeBrace]['line']) { 174 $error = 'Expected 0 spaces before closing brace; newline found'; 175 $phpcsFile->addError($error, $closeBrace, 'NewLineBeforeCloseBrace'); 176 } else { 177 $error = 'Expected 0 spaces before closing brace; %s found'; 178 $data = array($spaces); 179 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpaceBeforeCloseBrace', $data); 180 if ($fix === true) { 181 $phpcsFile->fixer->replaceToken(($closeBrace - 1), ''); 182 } 183 } 184 } 185 } 186 }//end if 187 188 if ($difference !== -1 && $difference !== 1) { 189 $error = 'Closing brace of a %s must be followed by a single blank line; found %s'; 190 $data = array( 191 $tokens[$stackPtr]['content'], 192 $difference, 193 ); 194 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'NewlinesAfterCloseBrace', $data); 195 if ($fix === true) { 196 if ($difference === 0) { 197 $first = $phpcsFile->findFirstOnLine(array(), $nextContent, true); 198 $phpcsFile->fixer->addNewlineBefore($first); 199 } else { 200 $phpcsFile->fixer->beginChangeset(); 201 for ($i = ($closeBrace + 1); $i < $nextContent; $i++) { 202 if ($tokens[$i]['line'] <= ($tokens[$closeBrace]['line'] + 1)) { 203 continue; 204 } else if ($tokens[$i]['line'] === $tokens[$nextContent]['line']) { 205 break; 206 } 207 208 $phpcsFile->fixer->replaceToken($i, ''); 209 } 210 211 $phpcsFile->fixer->endChangeset(); 212 } 213 } 214 }//end if 215 216 }//end processClose() 217 218 219}//end class 220