PHP_CodeSniffer
[ class tree: PHP_CodeSniffer ] [ index: PHP_CodeSniffer ] [ all elements ]

Source for file BlockCommentSniff.php

Documentation is available at BlockCommentSniff.php

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

Documentation generated on Mon, 11 Mar 2019 15:27:15 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.