1<?php 2/** 3 * Squiz_Sniffs_WhiteSpace_FunctionClosingBraceSpaceSniff. 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 16/** 17 * Squiz_Sniffs_WhiteSpace_FunctionClosingBraceSpaceSniff. 18 * 19 * Checks that there is one empty line before the closing brace of a function. 20 * 21 * @category PHP 22 * @package PHP_CodeSniffer 23 * @author Greg Sherwood <gsherwood@squiz.net> 24 * @author Marc McIntyre <mmcintyre@squiz.net> 25 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 26 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 27 * @version Release: @package_version@ 28 * @link http://pear.php.net/package/PHP_CodeSniffer 29 */ 30class Squiz_Sniffs_WhiteSpace_FunctionClosingBraceSpaceSniff implements PHP_CodeSniffer_Sniff 31{ 32 33 /** 34 * A list of tokenizers this sniff supports. 35 * 36 * @var array 37 */ 38 public $supportedTokenizers = array( 39 'PHP', 40 'JS', 41 ); 42 43 44 /** 45 * Returns an array of tokens this test wants to listen for. 46 * 47 * @return array 48 */ 49 public function register() 50 { 51 return array( 52 T_FUNCTION, 53 T_CLOSURE, 54 ); 55 56 }//end register() 57 58 59 /** 60 * Processes this test, when one of its tokens is encountered. 61 * 62 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 63 * @param int $stackPtr The position of the current token 64 * in the stack passed in $tokens. 65 * 66 * @return void 67 */ 68 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 69 { 70 $tokens = $phpcsFile->getTokens(); 71 72 if (isset($tokens[$stackPtr]['scope_closer']) === false) { 73 // Probably an interface method. 74 return; 75 } 76 77 $closeBrace = $tokens[$stackPtr]['scope_closer']; 78 $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true); 79 80 // Special case for empty JS functions. 81 if ($phpcsFile->tokenizerType === 'JS' && $prevContent === $tokens[$stackPtr]['scope_opener']) { 82 // In this case, the opening and closing brace must be 83 // right next to each other. 84 if ($tokens[$stackPtr]['scope_closer'] !== ($tokens[$stackPtr]['scope_opener'] + 1)) { 85 $error = 'The opening and closing braces of empty functions must be directly next to each other; e.g., function () {}'; 86 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBetween'); 87 if ($fix === true) { 88 $phpcsFile->fixer->beginChangeset(); 89 for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $closeBrace; $i++) { 90 $phpcsFile->fixer->replaceToken($i, ''); 91 } 92 93 $phpcsFile->fixer->endChangeset(); 94 } 95 } 96 97 return; 98 } 99 100 $nestedFunction = false; 101 if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true 102 || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true 103 || isset($tokens[$stackPtr]['nested_parenthesis']) === true 104 ) { 105 $nestedFunction = true; 106 } 107 108 $braceLine = $tokens[$closeBrace]['line']; 109 $prevLine = $tokens[$prevContent]['line']; 110 $found = ($braceLine - $prevLine - 1); 111 112 $afterKeyword = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); 113 $beforeKeyword = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true); 114 if ($nestedFunction === true) { 115 if ($found < 0) { 116 $error = 'Closing brace of nested function must be on a new line'; 117 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'ContentBeforeClose'); 118 if ($fix === true) { 119 $phpcsFile->fixer->addNewlineBefore($closeBrace); 120 } 121 } else if ($found > 0) { 122 $error = 'Expected 0 blank lines before closing brace of nested function; %s found'; 123 $data = array($found); 124 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeNestedClose', $data); 125 126 if ($fix === true) { 127 $phpcsFile->fixer->beginChangeset(); 128 $changeMade = false; 129 for ($i = ($prevContent + 1); $i < $closeBrace; $i++) { 130 // Try and maintain indentation. 131 if ($tokens[$i]['line'] === ($braceLine - 1)) { 132 break; 133 } 134 135 $phpcsFile->fixer->replaceToken($i, ''); 136 $changeMade = true; 137 } 138 139 // Special case for when the last content contains the newline 140 // token as well, like with a comment. 141 if ($changeMade === false) { 142 $phpcsFile->fixer->replaceToken(($prevContent + 1), ''); 143 } 144 145 $phpcsFile->fixer->endChangeset(); 146 }//end if 147 }//end if 148 } else { 149 if ($found !== 1) { 150 if ($found < 0) { 151 $found = 0; 152 } 153 154 $error = 'Expected 1 blank line before closing function brace; %s found'; 155 $data = array($found); 156 $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeClose', $data); 157 158 if ($fix === true) { 159 if ($found > 1) { 160 $phpcsFile->fixer->beginChangeset(); 161 for ($i = ($prevContent + 1); $i < ($closeBrace - 1); $i++) { 162 $phpcsFile->fixer->replaceToken($i, ''); 163 } 164 165 $phpcsFile->fixer->replaceToken($i, $phpcsFile->eolChar); 166 $phpcsFile->fixer->endChangeset(); 167 } else { 168 // Try and maintain indentation. 169 if ($tokens[($closeBrace - 1)]['code'] === T_WHITESPACE) { 170 $phpcsFile->fixer->addNewlineBefore($closeBrace - 1); 171 } else { 172 $phpcsFile->fixer->addNewlineBefore($closeBrace); 173 } 174 } 175 } 176 }//end if 177 }//end if 178 179 }//end process() 180 181 182}//end class 183