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

Source for file UseDeclarationSniff.php

Documentation is available at UseDeclarationSniff.php

  1. <?php
  2. /**
  3.  * Ensures USE blocks are declared correctly.
  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\PSR2\Sniffs\Namespaces;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class UseDeclarationSniff implements Sniff
  17. {
  18.  
  19.  
  20.     /**
  21.      * Returns an array of tokens this test wants to listen for.
  22.      *
  23.      * @return array 
  24.      */
  25.     public function register()
  26.     {
  27.         return array(T_USE);
  28.  
  29.     }//end register()
  30.  
  31.  
  32.     /**
  33.      * Processes this test, when one of its tokens is encountered.
  34.      *
  35.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  36.      * @param int                         $stackPtr  The position of the current token in
  37.      *                                                the stack passed in $tokens.
  38.      *
  39.      * @return void 
  40.      */
  41.     public function process(File $phpcsFile, $stackPtr)
  42.     {
  43.         if ($this->shouldIgnoreUse($phpcsFile, $stackPtr) === true) {
  44.             return;
  45.         }
  46.  
  47.         $tokens = $phpcsFile->getTokens();
  48.  
  49.         // One space after the use keyword.
  50.         if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
  51.             $error = 'There must be a single space after the USE keyword';
  52.             $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterUse');
  53.             if ($fix === true) {
  54.                 $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
  55.             }
  56.         }
  57.  
  58.         // Only one USE declaration allowed per statement.
  59.         $next = $phpcsFile->findNext(array(T_COMMA, T_SEMICOLON, T_OPEN_USE_GROUP, T_CLOSE_TAG), ($stackPtr + 1));
  60.         if ($next !== false
  61.             && $tokens[$next]['code'] !== T_SEMICOLON
  62.             && $tokens[$next]['code'] !== T_CLOSE_TAG
  63.         ) {
  64.             $error = 'There must be one USE keyword per declaration';
  65.             $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'MultipleDeclarations');
  66.             if ($fix === true) {
  67.                 if ($tokens[$next]['code'] === T_COMMA) {
  68.                     $phpcsFile->fixer->replaceToken($next, ';'.$phpcsFile->eolChar.'use ');
  69.                 } else {
  70.                     $baseUse      = rtrim($phpcsFile->getTokensAsString($stackPtr, ($next - $stackPtr)));
  71.                     $closingCurly = $phpcsFile->findNext(T_CLOSE_USE_GROUP, ($next + 1));
  72.  
  73.                     $phpcsFile->fixer->beginChangeset();
  74.  
  75.                     // Remove base use statement.
  76.                     for ($i = $stackPtr; $i <= $next; $i++) {
  77.                         $phpcsFile->fixer->replaceToken($i, '');
  78.                     }
  79.  
  80.                     // Convert grouped use statements into full use statements.
  81.                     do {
  82.                         $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), $closingCurly, true);
  83.  
  84.                         $whitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($next - 1), null, true);
  85.                         for ($i = ($whitespace + 1); $i < $next; $i++) {
  86.                             $phpcsFile->fixer->replaceToken($i, '');
  87.                         }
  88.  
  89.                         if ($tokens[$next]['code'] === T_CONST || $tokens[$next]['code'] === T_FUNCTION) {
  90.                             $phpcsFile->fixer->addContentBefore($next, 'use ');
  91.                             $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), $closingCurly, true);
  92.                             $phpcsFile->fixer->addContentBefore($next, str_replace('use ', '', $baseUse));
  93.                         } else {
  94.                             $phpcsFile->fixer->addContentBefore($next, $baseUse);
  95.                         }
  96.  
  97.                         $next = $phpcsFile->findNext(T_COMMA, ($next + 1), $closingCurly);
  98.                         if ($next !== false) {
  99.                             $phpcsFile->fixer->replaceToken($next, ';'.$phpcsFile->eolChar);
  100.                         }
  101.                     } while ($next !== false);
  102.  
  103.                     $phpcsFile->fixer->replaceToken($closingCurly, '');
  104.  
  105.                     // Remove any trailing whitespace.
  106.                     $next       = $phpcsFile->findNext(T_SEMICOLON, $closingCurly);
  107.                     $whitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($closingCurly - 1), null, true);
  108.                     for ($i = ($whitespace + 1); $i < $next; $i++) {
  109.                         $phpcsFile->fixer->replaceToken($i, '');
  110.                     }
  111.  
  112.                     $phpcsFile->fixer->endChangeset();
  113.                 }//end if
  114.             }//end if
  115.         }//end if
  116.  
  117.         // Make sure this USE comes after the first namespace declaration.
  118.         $prev = $phpcsFile->findPrevious(T_NAMESPACE, ($stackPtr - 1));
  119.         if ($prev !== false) {
  120.             $first = $phpcsFile->findNext(T_NAMESPACE, 1);
  121.             if ($prev !== $first) {
  122.                 $error = 'USE declarations must go after the first namespace declaration';
  123.                 $phpcsFile->addError($error, $stackPtr, 'UseAfterNamespace');
  124.             }
  125.         }
  126.  
  127.         // Only interested in the last USE statement from here onwards.
  128.         $nextUse = $phpcsFile->findNext(T_USE, ($stackPtr + 1));
  129.         while ($this->shouldIgnoreUse($phpcsFile, $nextUse) === true) {
  130.             $nextUse = $phpcsFile->findNext(T_USE, ($nextUse + 1));
  131.             if ($nextUse === false) {
  132.                 break;
  133.             }
  134.         }
  135.  
  136.         if ($nextUse !== false) {
  137.             return;
  138.         }
  139.  
  140.         $end = $phpcsFile->findNext(T_SEMICOLON, ($stackPtr + 1));
  141.         if ($end === false) {
  142.             return;
  143.         }
  144.  
  145.         $next = $phpcsFile->findNext(T_WHITESPACE, ($end + 1), null, true);
  146.  
  147.         if ($next === false || $tokens[$next]['code'] === T_CLOSE_TAG) {
  148.             return;
  149.         }
  150.  
  151.         $diff = ($tokens[$next]['line'] - $tokens[$end]['line'] - 1);
  152.         if ($diff !== 1) {
  153.             if ($diff < 0) {
  154.                 $diff = 0;
  155.             }
  156.  
  157.             $error = 'There must be one blank line after the last USE statement; %s found;';
  158.             $data  = array($diff);
  159.             $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterLastUse', $data);
  160.             if ($fix === true) {
  161.                 if ($diff === 0) {
  162.                     $phpcsFile->fixer->addNewline($end);
  163.                 } else {
  164.                     $phpcsFile->fixer->beginChangeset();
  165.                     for ($i = ($end + 1); $i < $next; $i++) {
  166.                         if ($tokens[$i]['line'] === $tokens[$next]['line']) {
  167.                             break;
  168.                         }
  169.  
  170.                         $phpcsFile->fixer->replaceToken($i, '');
  171.                     }
  172.  
  173.                     $phpcsFile->fixer->addNewline($end);
  174.                     $phpcsFile->fixer->endChangeset();
  175.                 }
  176.             }
  177.         }//end if
  178.  
  179.     }//end process()
  180.  
  181.  
  182.     /**
  183.      * Check if this use statement is part of the namespace block.
  184.      *
  185.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  186.      * @param int                         $stackPtr  The position of the current token in
  187.      *                                                the stack passed in $tokens.
  188.      *
  189.      * @return void 
  190.      */
  191.     private function shouldIgnoreUse($phpcsFile, $stackPtr)
  192.     {
  193.         $tokens = $phpcsFile->getTokens();
  194.  
  195.         // Ignore USE keywords inside closures.
  196.         $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
  197.         if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
  198.             return true;
  199.         }
  200.  
  201.         // Ignore USE keywords for traits.
  202.         if ($phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_TRAIT)) === true) {
  203.             return true;
  204.         }
  205.  
  206.         return false;
  207.  
  208.     }//end shouldIgnoreUse()
  209.  
  210.  
  211. }//end class

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