1<?php 2/** 3 * Generic_Sniffs_Functions_FunctionCallArgumentSpacingSniff. 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 * Generic_Sniffs_Functions_FunctionCallArgumentSpacingSniff. 18 * 19 * Checks that calls to methods and functions are spaced correctly. 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 Generic_Sniffs_Functions_FunctionCallArgumentSpacingSniff implements PHP_CodeSniffer_Sniff 31{ 32 33 34 /** 35 * Returns an array of tokens this test wants to listen for. 36 * 37 * @return array 38 */ 39 public function register() 40 { 41 $tokens = PHP_CodeSniffer_Tokens::$functionNameTokens; 42 43 // For calling closures. 44 $tokens[] = T_VARIABLE; 45 46 return $tokens; 47 48 }//end register() 49 50 51 /** 52 * Processes this test, when one of its tokens is encountered. 53 * 54 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 55 * @param int $stackPtr The position of the current token in the 56 * stack passed in $tokens. 57 * 58 * @return void 59 */ 60 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 61 { 62 $tokens = $phpcsFile->getTokens(); 63 64 // Skip tokens that are the names of functions or classes 65 // within their definitions. For example: 66 // function myFunction... 67 // "myFunction" is T_STRING but we should skip because it is not a 68 // function or method *call*. 69 $functionName = $stackPtr; 70 $ignoreTokens = PHP_CodeSniffer_Tokens::$emptyTokens; 71 $ignoreTokens[] = T_BITWISE_AND; 72 $functionKeyword = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true); 73 if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) { 74 return; 75 } 76 77 // If the next non-whitespace token after the function or method call 78 // is not an opening parenthesis then it cant really be a *call*. 79 $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($functionName + 1), null, true); 80 if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { 81 return; 82 } 83 84 if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { 85 return; 86 } 87 88 $closeBracket = $tokens[$openBracket]['parenthesis_closer']; 89 $nextSeparator = $openBracket; 90 91 $find = array( 92 T_COMMA, 93 T_VARIABLE, 94 T_CLOSURE, 95 T_OPEN_SHORT_ARRAY, 96 ); 97 98 while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) { 99 if ($tokens[$nextSeparator]['code'] === T_CLOSURE) { 100 // Skip closures. 101 $nextSeparator = $tokens[$nextSeparator]['scope_closer']; 102 continue; 103 } else if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) { 104 // Skips arrays using short notation. 105 $nextSeparator = $tokens[$nextSeparator]['bracket_closer']; 106 continue; 107 } 108 109 // Make sure the comma or variable belongs directly to this function call, 110 // and is not inside a nested function call or array. 111 $brackets = $tokens[$nextSeparator]['nested_parenthesis']; 112 $lastBracket = array_pop($brackets); 113 if ($lastBracket !== $closeBracket) { 114 continue; 115 } 116 117 if ($tokens[$nextSeparator]['code'] === T_COMMA) { 118 if ($tokens[($nextSeparator - 1)]['code'] === T_WHITESPACE) { 119 $prev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextSeparator - 2), null, true); 120 if (isset(PHP_CodeSniffer_Tokens::$heredocTokens[$tokens[$prev]['code']]) === false) { 121 $error = 'Space found before comma in function call'; 122 $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'SpaceBeforeComma'); 123 if ($fix === true) { 124 $phpcsFile->fixer->replaceToken(($nextSeparator - 1), ''); 125 } 126 } 127 } 128 129 if ($tokens[($nextSeparator + 1)]['code'] !== T_WHITESPACE) { 130 $error = 'No space found after comma in function call'; 131 $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'NoSpaceAfterComma'); 132 if ($fix === true) { 133 $phpcsFile->fixer->addContent($nextSeparator, ' '); 134 } 135 } else { 136 // If there is a newline in the space, then they must be formatting 137 // each argument on a newline, which is valid, so ignore it. 138 $next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextSeparator + 1), null, true); 139 if ($tokens[$next]['line'] === $tokens[$nextSeparator]['line']) { 140 $space = strlen($tokens[($nextSeparator + 1)]['content']); 141 if ($space > 1) { 142 $error = 'Expected 1 space after comma in function call; %s found'; 143 $data = array($space); 144 $fix = $phpcsFile->addFixableError($error, $nextSeparator, 'TooMuchSpaceAfterComma', $data); 145 if ($fix === true) { 146 $phpcsFile->fixer->replaceToken(($nextSeparator + 1), ' '); 147 } 148 } 149 } 150 }//end if 151 } else { 152 // Token is a variable. 153 $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextSeparator + 1), $closeBracket, true); 154 if ($nextToken !== false) { 155 if ($tokens[$nextToken]['code'] === T_EQUAL) { 156 if (($tokens[($nextToken - 1)]['code']) !== T_WHITESPACE) { 157 $error = 'Expected 1 space before = sign of default value'; 158 $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceBeforeEquals'); 159 if ($fix === true) { 160 $phpcsFile->fixer->addContentBefore($nextToken, ' '); 161 } 162 } 163 164 if ($tokens[($nextToken + 1)]['code'] !== T_WHITESPACE) { 165 $error = 'Expected 1 space after = sign of default value'; 166 $fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceAfterEquals'); 167 if ($fix === true) { 168 $phpcsFile->fixer->addContent($nextToken, ' '); 169 } 170 } 171 } 172 } 173 }//end if 174 }//end while 175 176 }//end process() 177 178 179}//end class 180