PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/CodeSniffer/Standards/Squiz/Sniffs/Commenting/BlockCommentSniff.php

https://gitlab.com/0072016/PHP_CodeSniffer
PHP | 357 lines | 247 code | 47 blank | 63 comment | 59 complexity | c89cec5c6289f33c806a506486003f4d MD5 | raw file
  1. <?php
  2. /**
  3. * Squiz_Sniffs_Commenting_BlockCommentSniff.
  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. * Squiz_Sniffs_Commenting_BlockCommentSniff.
  17. *
  18. * Verifies that block comments are used appropriately.
  19. *
  20. * @category PHP
  21. * @package PHP_CodeSniffer
  22. * @author Greg Sherwood <gsherwood@squiz.net>
  23. * @author Marc McIntyre <mmcintyre@squiz.net>
  24. * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
  25. * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  26. * @version Release: @package_version@
  27. * @link http://pear.php.net/package/PHP_CodeSniffer
  28. */
  29. class Squiz_Sniffs_Commenting_BlockCommentSniff implements PHP_CodeSniffer_Sniff
  30. {
  31. /**
  32. * The --tab-width CLI value that is being used.
  33. *
  34. * @var int
  35. */
  36. private $_tabWidth = null;
  37. /**
  38. * Returns an array of tokens this test wants to listen for.
  39. *
  40. * @return array
  41. */
  42. public function register()
  43. {
  44. return array(
  45. T_COMMENT,
  46. T_DOC_COMMENT_OPEN_TAG,
  47. );
  48. }//end register()
  49. /**
  50. * Processes this test, when one of its tokens is encountered.
  51. *
  52. * @param PHP_CodeSniffer_File $phpcsFile The current file being scanned.
  53. * @param int $stackPtr The position of the current token in the
  54. * stack passed in $tokens.
  55. *
  56. * @return void
  57. */
  58. public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
  59. {
  60. if ($this->_tabWidth === null) {
  61. $cliValues = $phpcsFile->phpcs->cli->getCommandLineValues();
  62. if (isset($cliValues['tabWidth']) === false || $cliValues['tabWidth'] === 0) {
  63. // We have no idea how wide tabs are, so assume 4 spaces for fixing.
  64. $this->_tabWidth = 4;
  65. } else {
  66. $this->_tabWidth = $cliValues['tabWidth'];
  67. }
  68. }
  69. $tokens = $phpcsFile->getTokens();
  70. // If it's an inline comment, return.
  71. if (substr($tokens[$stackPtr]['content'], 0, 2) !== '/*') {
  72. return;
  73. }
  74. // If this is a function/class/interface doc block comment, skip it.
  75. // We are only interested in inline doc block comments.
  76. if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
  77. $nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
  78. $ignore = array(
  79. T_CLASS => true,
  80. T_INTERFACE => true,
  81. T_TRAIT => true,
  82. T_FUNCTION => true,
  83. T_PUBLIC => true,
  84. T_PRIVATE => true,
  85. T_FINAL => true,
  86. T_PROTECTED => true,
  87. T_STATIC => true,
  88. T_ABSTRACT => true,
  89. T_CONST => true,
  90. T_VAR => true,
  91. );
  92. if (isset($ignore[$tokens[$nextToken]['code']]) === true) {
  93. return;
  94. }
  95. $prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
  96. if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
  97. return;
  98. }
  99. $error = 'Block comments must be started with /*';
  100. $fix = $phpcsFile->addFixableError($error, $stackPtr, 'WrongStart');
  101. if ($fix === true) {
  102. $phpcsFile->fixer->replaceToken($stackPtr, '/*');
  103. }
  104. $end = $tokens[$stackPtr]['comment_closer'];
  105. if ($tokens[$end]['content'] !== '*/') {
  106. $error = 'Block comments must be ended with */';
  107. $fix = $phpcsFile->addFixableError($error, $end, 'WrongEnd');
  108. if ($fix === true) {
  109. $phpcsFile->fixer->replaceToken($end, '*/');
  110. }
  111. }
  112. return;
  113. }//end if
  114. $commentLines = array($stackPtr);
  115. $nextComment = $stackPtr;
  116. $lastLine = $tokens[$stackPtr]['line'];
  117. $commentString = $tokens[$stackPtr]['content'];
  118. // Construct the comment into an array.
  119. while (($nextComment = $phpcsFile->findNext(T_WHITESPACE, ($nextComment + 1), null, true)) !== false) {
  120. if ($tokens[$nextComment]['code'] !== $tokens[$stackPtr]['code']) {
  121. // Found the next bit of code.
  122. break;
  123. }
  124. if (($tokens[$nextComment]['line'] - 1) !== $lastLine) {
  125. // Not part of the block.
  126. break;
  127. }
  128. $lastLine = $tokens[$nextComment]['line'];
  129. $commentLines[] = $nextComment;
  130. $commentString .= $tokens[$nextComment]['content'];
  131. if ($tokens[$nextComment]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
  132. break;
  133. }
  134. }
  135. $commentText = str_replace($phpcsFile->eolChar, '', $commentString);
  136. $commentText = trim($commentText, '/* ');
  137. if ($commentText === '') {
  138. $error = 'Empty block comment not allowed';
  139. $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
  140. if ($fix === true) {
  141. $phpcsFile->fixer->beginChangeset();
  142. $phpcsFile->fixer->replaceToken($stackPtr, '');
  143. $lastToken = array_pop($commentLines);
  144. for ($i = ($stackPtr + 1); $i <= $lastToken; $i++) {
  145. $phpcsFile->fixer->replaceToken($i, '');
  146. }
  147. $phpcsFile->fixer->endChangeset();
  148. }
  149. return;
  150. }
  151. if (count($commentLines) === 1) {
  152. $error = 'Single line block comment not allowed; use inline ("// text") comment instead';
  153. $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SingleLine');
  154. if ($fix === true) {
  155. $comment = '// '.$commentText.$phpcsFile->eolChar;
  156. $phpcsFile->fixer->replaceToken($stackPtr, $comment);
  157. }
  158. return;
  159. }
  160. $content = trim($tokens[$stackPtr]['content']);
  161. if ($content !== '/*' && $content !== '/**') {
  162. $error = 'Block comment text must start on a new line';
  163. $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoNewLine');
  164. if ($fix === true) {
  165. $indent = '';
  166. if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
  167. if (isset($tokens[($stackPtr - 1)]['orig_content']) === true) {
  168. $indent = $tokens[($stackPtr - 1)]['orig_content'];
  169. } else {
  170. $indent = $tokens[($stackPtr - 1)]['content'];
  171. }
  172. }
  173. $comment = preg_replace(
  174. '/^(\s*\/\*\*?)/',
  175. '$1'.$phpcsFile->eolChar.$indent,
  176. $tokens[$stackPtr]['content'],
  177. 1
  178. );
  179. $phpcsFile->fixer->replaceToken($stackPtr, $comment);
  180. }
  181. return;
  182. }//end if
  183. $starColumn = ($tokens[$stackPtr]['column'] + 3);
  184. // Make sure first line isn't blank.
  185. if (trim($tokens[$commentLines[1]]['content']) === '') {
  186. $error = 'Empty line not allowed at start of comment';
  187. $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'HasEmptyLine');
  188. if ($fix === true) {
  189. $phpcsFile->fixer->replaceToken($commentLines[1], '');
  190. }
  191. } else {
  192. // Check indentation of first line.
  193. $content = $tokens[$commentLines[1]]['content'];
  194. $commentText = ltrim($content);
  195. $leadingSpace = (strlen($content) - strlen($commentText));
  196. if ($leadingSpace !== $starColumn) {
  197. $expected = $starColumn.' space';
  198. if ($starColumn !== 1) {
  199. $expected .= 's';
  200. }
  201. $data = array(
  202. $expected,
  203. $leadingSpace,
  204. );
  205. $error = 'First line of comment not aligned correctly; expected %s but found %s';
  206. $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'FirstLineIndent', $data);
  207. if ($fix === true) {
  208. if (isset($tokens[$commentLines[1]]['orig_content']) === true
  209. && $tokens[$commentLines[1]]['orig_content'][0] === "\t"
  210. ) {
  211. // Line is indented using tabs.
  212. $padding = str_repeat("\t", floor($starColumn / $this->_tabWidth));
  213. } else {
  214. $padding = str_repeat(' ', $starColumn);
  215. }
  216. $phpcsFile->fixer->replaceToken($commentLines[1], $padding.ltrim($content));
  217. }
  218. }//end if
  219. if (preg_match('/^\p{Ll}/u', $commentText) === 1) {
  220. $error = 'Block comments must start with a capital letter';
  221. $phpcsFile->addError($error, $commentLines[1], 'NoCapital');
  222. }
  223. }//end if
  224. // Check that each line of the comment is indented past the star.
  225. foreach ($commentLines as $line) {
  226. $leadingSpace = (strlen($tokens[$line]['content']) - strlen(ltrim($tokens[$line]['content'])));
  227. // First and last lines (comment opener and closer) are handled separately.
  228. if ($line === $commentLines[(count($commentLines) - 1)] || $line === $commentLines[0]) {
  229. continue;
  230. }
  231. // First comment line was handled above.
  232. if ($line === $commentLines[1]) {
  233. continue;
  234. }
  235. // If it's empty, continue.
  236. if (trim($tokens[$line]['content']) === '') {
  237. continue;
  238. }
  239. if ($leadingSpace < $starColumn) {
  240. $expected = $starColumn.' space';
  241. if ($starColumn !== 1) {
  242. $expected .= 's';
  243. }
  244. $data = array(
  245. $expected,
  246. $leadingSpace,
  247. );
  248. $error = 'Comment line indented incorrectly; expected at least %s but found %s';
  249. $fix = $phpcsFile->addFixableError($error, $line, 'LineIndent', $data);
  250. if ($fix === true) {
  251. if (isset($tokens[$line]['orig_content']) === true
  252. && $tokens[$line]['orig_content'][0] === "\t"
  253. ) {
  254. // Line is indented using tabs.
  255. $padding = str_repeat("\t", floor($starColumn / $this->_tabWidth));
  256. } else {
  257. $padding = str_repeat(' ', $starColumn);
  258. }
  259. $phpcsFile->fixer->replaceToken($line, $padding.ltrim($tokens[$line]['content']));
  260. }
  261. }//end if
  262. }//end foreach
  263. // Finally, test the last line is correct.
  264. $lastIndex = (count($commentLines) - 1);
  265. $content = trim($tokens[$commentLines[$lastIndex]]['content']);
  266. if ($content !== '*/' && $content !== '**/') {
  267. $error = 'Comment closer must be on a new line';
  268. $phpcsFile->addError($error, $commentLines[$lastIndex]);
  269. } else {
  270. $content = $tokens[$commentLines[$lastIndex]]['content'];
  271. $commentText = ltrim($content);
  272. $leadingSpace = (strlen($content) - strlen($commentText));
  273. if ($leadingSpace !== ($tokens[$stackPtr]['column'] - 1)) {
  274. $expected = ($tokens[$stackPtr]['column'] - 1);
  275. if ($expected === 1) {
  276. $expected .= ' space';
  277. } else {
  278. $expected .= ' spaces';
  279. }
  280. $data = array(
  281. $expected,
  282. $leadingSpace,
  283. );
  284. $error = 'Last line of comment aligned incorrectly; expected %s but found %s';
  285. $phpcsFile->addError($error, $commentLines[$lastIndex], 'LastLineIndent', $data);
  286. }
  287. }//end if
  288. // Check that the lines before and after this comment are blank.
  289. $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
  290. if (isset($tokens[$contentBefore]['scope_closer']) === true
  291. && $tokens[$contentBefore]['scope_opener'] === $contentBefore
  292. ) {
  293. if (($tokens[$stackPtr]['line'] - $tokens[$contentBefore]['line']) !== 1) {
  294. $error = 'Empty line not required before block comment';
  295. $phpcsFile->addError($error, $stackPtr, 'HasEmptyLineBefore');
  296. }
  297. } else {
  298. if (($tokens[$stackPtr]['line'] - $tokens[$contentBefore]['line']) < 2) {
  299. $error = 'Empty line required before block comment';
  300. $phpcsFile->addError($error, $stackPtr, 'NoEmptyLineBefore');
  301. }
  302. }
  303. $commentCloser = $commentLines[$lastIndex];
  304. $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($commentCloser + 1), null, true);
  305. if ($contentAfter !== false && ($tokens[$contentAfter]['line'] - $tokens[$commentCloser]['line']) < 2) {
  306. $error = 'Empty line required after block comment';
  307. $phpcsFile->addError($error, $commentCloser, 'NoEmptyLineAfter');
  308. }
  309. }//end process()
  310. }//end class