/Sniffs/Functions/FunctionCallSignatureSniff.php

https://github.com/SeanJA/PHP_CodeSniffer-Drupal-Standard · PHP · 245 lines · 116 code · 38 blank · 91 comment · 30 complexity · 1fb790f68313a69ca09e837ae91c1568 MD5 · raw file

  1. <?php
  2. /**
  3. * Drupal_Sniffs_Functions_FunctionCallSignatureSniff.
  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 Squiz Pty Ltd (ABN 77 084 670 600)
  12. * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  13. * @version CVS: $Id: FunctionCallSignatureSniff.php,v 1.7 2008/12/05 02:45:08 squiz Exp $
  14. * @link http://pear.php.net/package/PHP_CodeSniffer
  15. */
  16. /**
  17. * PEAR_Sniffs_Functions_FunctionCallSignatureSniff.
  18. *
  19. * @category PHP
  20. * @package PHP_CodeSniffer
  21. * @author Greg Sherwood <gsherwood@squiz.net>
  22. * @author Marc McIntyre <mmcintyre@squiz.net>
  23. * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
  24. * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  25. * @version Release: 1.2.0RC3
  26. * @link http://pear.php.net/package/PHP_CodeSniffer
  27. */
  28. class Drupal_Sniffs_Functions_FunctionCallSignatureSniff implements PHP_CodeSniffer_Sniff
  29. {
  30. /**
  31. * Returns an array of tokens this test wants to listen for.
  32. *
  33. * @return array
  34. */
  35. public function register()
  36. {
  37. return array(T_STRING);
  38. }//end register()
  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. $tokens = $phpcsFile->getTokens();
  51. // Find the next non-empty token.
  52. $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
  53. if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
  54. // Not a function call.
  55. return;
  56. }
  57. if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
  58. // Not a function call.
  59. return;
  60. }
  61. // Find the previous non-empty token.
  62. $previous = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
  63. if ($tokens[$previous]['code'] === T_FUNCTION) {
  64. // It's a function definition, not a function call.
  65. return;
  66. }
  67. if ($tokens[$previous]['code'] === T_NEW) {
  68. // We are creating an object, not calling a function.
  69. return;
  70. }
  71. $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
  72. if (($stackPtr + 1) !== $openBracket) {
  73. // Checking this: $value = my_function[*](...).
  74. $error = 'Space before opening parenthesis of function call prohibited';
  75. $phpcsFile->addError($error, $stackPtr);
  76. }
  77. $next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true);
  78. if ($tokens[$next]['code'] === T_SEMICOLON) {
  79. if (in_array($tokens[($closeBracket + 1)]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
  80. $error = 'Space after closing parenthesis of function call prohibited';
  81. $phpcsFile->addError($error, $closeBracket);
  82. }
  83. }
  84. // Check if this is a single line or multi-line function call.
  85. if ($tokens[$openBracket]['line'] === $tokens[$closeBracket]['line']) {
  86. $this->processSingleLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
  87. } else {
  88. $this->processMultiLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
  89. }
  90. }//end process()
  91. /**
  92. * Processes single-line calls.
  93. *
  94. * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  95. * @param int $stackPtr The position of the current token
  96. * in the stack passed in $tokens.
  97. * @param int $openBracket The position of the openning bracket
  98. * in the stack passed in $tokens.
  99. * @param array $tokens The stack of tokens that make up
  100. * the file.
  101. *
  102. * @return void
  103. */
  104. public function processSingleLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens)
  105. {
  106. if ($tokens[($openBracket + 1)]['code'] === T_WHITESPACE) {
  107. // Checking this: $value = my_function([*]...).
  108. $error = 'Space after opening parenthesis of function call prohibited';
  109. $phpcsFile->addError($error, $stackPtr);
  110. }
  111. $closer = $tokens[$openBracket]['parenthesis_closer'];
  112. if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) {
  113. // Checking this: $value = my_function(...[*]).
  114. $between = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
  115. // Only throw an error if there is some content between the parenthesis.
  116. // i.e., Checking for this: $value = my_function().
  117. // If there is no content, then we would have thrown an error in the
  118. // previous IF statement because it would look like this:
  119. // $value = my_function( ).
  120. if ($between !== $closer) {
  121. $error = 'Space before closing parenthesis of function call prohibited';
  122. $phpcsFile->addError($error, $closer);
  123. }
  124. }
  125. }//end processSingleLineCall()
  126. /**
  127. * Processes multi-line calls.
  128. *
  129. * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  130. * @param int $stackPtr The position of the current token
  131. * in the stack passed in $tokens.
  132. * @param int $openBracket The position of the openning bracket
  133. * in the stack passed in $tokens.
  134. * @param array $tokens The stack of tokens that make up
  135. * the file.
  136. *
  137. * @return void
  138. */
  139. public function processMultiLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens)
  140. {
  141. // We need to work out how far indented the function
  142. // call itself is, so we can work out how far to
  143. // indent the arguments.
  144. $functionIndent = 0;
  145. for ($i = ($stackPtr - 1); $i >= 0; $i--) {
  146. if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
  147. $i++;
  148. break;
  149. }
  150. }
  151. if ($tokens[$i]['code'] === T_WHITESPACE) {
  152. $functionIndent = strlen($tokens[$i]['content']);
  153. }
  154. // Each line between the parenthesis should be indented 4 spaces.
  155. $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
  156. $lastLine = $tokens[$openBracket]['line'];
  157. for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
  158. // Skip nested function calls.
  159. if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
  160. $i = $tokens[$i]['parenthesis_closer'];
  161. $lastLine = $tokens[$i]['line'];
  162. continue;
  163. }
  164. if ($tokens[$i]['line'] !== $lastLine) {
  165. $lastLine = $tokens[$i]['line'];
  166. // We changed lines, so this should be a whitespace indent token.
  167. if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$heredocTokens) === true) {
  168. // Ignore heredoc indentation.
  169. continue;
  170. }
  171. if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
  172. if ($tokens[$i]['code'] === $tokens[($i - 1)]['code']) {
  173. // Ignore multi-line string indentation.
  174. continue;
  175. }
  176. }
  177. if ($tokens[$i]['line'] === $tokens[$closeBracket]['line']) {
  178. // Closing brace needs to be indented to the same level
  179. // as the function call.
  180. $expectedIndent = $functionIndent;
  181. } else {
  182. $expectedIndent = ($functionIndent + 2);
  183. }
  184. if ($tokens[$i]['code'] !== T_WHITESPACE) {
  185. $foundIndent = 0;
  186. } else {
  187. $foundIndent = strlen($tokens[$i]['content']);
  188. }
  189. if ($expectedIndent !== $foundIndent) {
  190. $error = "Multi-line function call not indented correctly; expected $expectedIndent spaces but found $foundIndent";
  191. $phpcsFile->addError($error, $i);
  192. }
  193. }//end if
  194. }//end for
  195. $arrayAsOpener = ($tokens[($openBracket + 1 )]['code'] == T_ARRAY);
  196. if ($tokens[($openBracket + 1)]['content'] !== $phpcsFile->eolChar && !$arrayAsOpener) {
  197. $error = 'Opening parenthesis of a multi-line function call and/or an array must be the last content on the line';
  198. $phpcsFile->addError($error, $stackPtr);
  199. }
  200. $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1 - (int)$arrayAsOpener), null, true);
  201. if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
  202. $error = 'Closing parenthesis of a multi-line function call and/or array must be on a line by themself';
  203. $phpcsFile->addError($error, $closeBracket);
  204. }
  205. }//end processMultiLineCall()
  206. }//end class
  207. ?>