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

Source for file MultiLineConditionSniff.php

Documentation is available at MultiLineConditionSniff.php

  1. <?php
  2. /**
  3.  * Ensure multi-line IF conditions are defined 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\PEAR\Sniffs\ControlStructures;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class MultiLineConditionSniff implements Sniff
  17. {
  18.  
  19.     /**
  20.      * A list of tokenizers this sniff supports.
  21.      *
  22.      * @var array 
  23.      */
  24.     public $supportedTokenizers = array(
  25.                                    'PHP',
  26.                                    'JS',
  27.                                   );
  28.  
  29.     /**
  30.      * The number of spaces code should be indented.
  31.      *
  32.      * @var integer 
  33.      */
  34.     public $indent = 4;
  35.  
  36.  
  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_IF,
  46.                 T_ELSEIF,
  47.                );
  48.  
  49.     }//end register()
  50.  
  51.  
  52.     /**
  53.      * Processes this test, when one of its tokens is encountered.
  54.      *
  55.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  56.      * @param int                         $stackPtr  The position of the current token
  57.      *                                                in the stack passed in $tokens.
  58.      *
  59.      * @return void 
  60.      */
  61.     public function process(File $phpcsFile$stackPtr)
  62.     {
  63.         $tokens $phpcsFile->getTokens();
  64.  
  65.         if (isset($tokens[$stackPtr]['parenthesis_opener']=== false{
  66.             return;
  67.         }
  68.  
  69.         $openBracket    $tokens[$stackPtr]['parenthesis_opener'];
  70.         $closeBracket   $tokens[$stackPtr]['parenthesis_closer'];
  71.         $spaceAfterOpen = 0;
  72.         if ($tokens[($openBracket + 1)]['code'=== T_WHITESPACE{
  73.             if (strpos($tokens[($openBracket + 1)]['content']$phpcsFile->eolChar!== false{
  74.                 $spaceAfterOpen 'newline';
  75.             else {
  76.                 $spaceAfterOpen strlen($tokens[($openBracket + 1)]['content']);
  77.             }
  78.         }
  79.  
  80.         if ($spaceAfterOpen !== 0{
  81.             $error 'First condition of a multi-line IF statement must directly follow the opening parenthesis';
  82.             $fix   $phpcsFile->addFixableError($error($openBracket + 1)'SpacingAfterOpenBrace');
  83.             if ($fix === true{
  84.                 if ($spaceAfterOpen === 'newline'{
  85.                     $phpcsFile->fixer->replaceToken(($openBracket + 1)'');
  86.                 else {
  87.                     $phpcsFile->fixer->replaceToken(($openBracket + 1)'');
  88.                 }
  89.             }
  90.         }
  91.  
  92.         // We need to work out how far indented the if statement
  93.         // itself is, so we can work out how far to indent conditions.
  94.         $statementIndent = 0;
  95.         for ($i ($stackPtr - 1)$i >= 0; $i--{
  96.             if ($tokens[$i]['line'!== $tokens[$stackPtr]['line']{
  97.                 $i++;
  98.                 break;
  99.             }
  100.         }
  101.  
  102.         if ($i >= 0 && $tokens[$i]['code'=== T_WHITESPACE{
  103.             $statementIndent strlen($tokens[$i]['content']);
  104.         }
  105.  
  106.         // Each line between the parenthesis should be indented 4 spaces
  107.         // and start with an operator, unless the line is inside a
  108.         // function call, in which case it is ignored.
  109.         $prevLine $tokens[$openBracket]['line'];
  110.         for ($i ($openBracket + 1)$i <= $closeBracket$i++{
  111.             if ($i === $closeBracket && $tokens[$openBracket]['line'!== $tokens[$i]['line']{
  112.                 $prev $phpcsFile->findPrevious(T_WHITESPACE($i - 1)nulltrue);
  113.                 if ($tokens[$prev]['line'=== $tokens[$i]['line']{
  114.                     // Closing bracket is on the same line as a condition.
  115.                     $error 'Closing parenthesis of a multi-line IF statement must be on a new line';
  116.                     $fix   $phpcsFile->addFixableError($error$closeBracket'CloseBracketNewLine');
  117.                     if ($fix === true{
  118.                         // Account for a comment at the end of the line.
  119.                         $next $phpcsFile->findNext(T_WHITESPACE($closeBracket + 1)nulltrue);
  120.                         if ($tokens[$next]['code'!== T_COMMENT{
  121.                             $phpcsFile->fixer->addNewlineBefore($closeBracket);
  122.                         else {
  123.                             $next $phpcsFile->findNext(Tokens::$emptyTokens($next + 1)nulltrue);
  124.                             $phpcsFile->fixer->beginChangeset();
  125.                             $phpcsFile->fixer->replaceToken($closeBracket'');
  126.                             $phpcsFile->fixer->addContentBefore($next')');
  127.                             $phpcsFile->fixer->endChangeset();
  128.                         }
  129.                     }
  130.                 }
  131.             }//end if
  132.  
  133.             if ($tokens[$i]['line'!== $prevLine{
  134.                 if ($tokens[$i]['line'=== $tokens[$closeBracket]['line']{
  135.                     $next $phpcsFile->findNext(T_WHITESPACE$inulltrue);
  136.                     if ($next !== $closeBracket{
  137.                         $expectedIndent ($statementIndent $this->indent);
  138.                     else {
  139.                         // Closing brace needs to be indented to the same level
  140.                         // as the statement.
  141.                         $expectedIndent $statementIndent;
  142.                     }//end if
  143.                 else {
  144.                     $expectedIndent ($statementIndent $this->indent);
  145.                 }//end if
  146.  
  147.                 if ($tokens[$i]['code'=== T_COMMENT{
  148.                     $prevLine $tokens[$i]['line'];
  149.                     continue;
  150.                 }
  151.  
  152.                 // We changed lines, so this should be a whitespace indent token.
  153.                 if ($tokens[$i]['code'!== T_WHITESPACE{
  154.                     $foundIndent = 0;
  155.                 else {
  156.                     $foundIndent strlen($tokens[$i]['content']);
  157.                 }
  158.  
  159.                 if ($expectedIndent !== $foundIndent{
  160.                     $error 'Multi-line IF statement not indented correctly; expected %s spaces but found %s';
  161.                     $data  = array(
  162.                               $expectedIndent,
  163.                               $foundIndent,
  164.                              );
  165.  
  166.                     $fix $phpcsFile->addFixableError($error$i'Alignment'$data);
  167.                     if ($fix === true{
  168.                         $spaces str_repeat(' '$expectedIndent);
  169.                         if ($foundIndent === 0{
  170.                             $phpcsFile->fixer->addContentBefore($i$spaces);
  171.                         else {
  172.                             $phpcsFile->fixer->replaceToken($i$spaces);
  173.                         }
  174.                     }
  175.                 }
  176.  
  177.                 $next $phpcsFile->findNext(Tokens::$emptyTokens$inulltrue);
  178.                 if ($next !== $closeBracket{
  179.                     if (isset(Tokens::$booleanOperators[$tokens[$next]['code']]=== false{
  180.                         $error 'Each line in a multi-line IF statement must begin with a boolean operator';
  181.                         $fix   $phpcsFile->addFixableError($error$i'StartWithBoolean');
  182.                         if ($fix === true{
  183.                             $prev $phpcsFile->findPrevious(Tokens::$emptyTokens($i - 1)$openBrackettrue);
  184.                             if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]=== true{
  185.                                 $phpcsFile->fixer->beginChangeset();
  186.                                 $phpcsFile->fixer->replaceToken($prev'');
  187.                                 $phpcsFile->fixer->addContentBefore($next$tokens[$prev]['content'].' ');
  188.                                 $phpcsFile->fixer->endChangeset();
  189.                             else {
  190.                                 for ($x ($prev + 1)$x $next$x++{
  191.                                     $phpcsFile->fixer->replaceToken($x'');
  192.                                 }
  193.                             }
  194.                         }
  195.                     }
  196.                 }//end if
  197.  
  198.                 $prevLine $tokens[$i]['line'];
  199.             }//end if
  200.  
  201.             if ($tokens[$i]['code'=== T_STRING{
  202.                 $next $phpcsFile->findNext(T_WHITESPACE($i + 1)nulltrue);
  203.                 if ($tokens[$next]['code'=== T_OPEN_PARENTHESIS{
  204.                     // This is a function call, so skip to the end as they
  205.                     // have their own indentation rules.
  206.                     $i        $tokens[$next]['parenthesis_closer'];
  207.                     $prevLine $tokens[$i]['line'];
  208.                     continue;
  209.                 }
  210.             }
  211.         }//end for
  212.  
  213.         // From here on, we are checking the spacing of the opening and closing
  214.         // braces. If this IF statement does not use braces, we end here.
  215.         if (isset($tokens[$stackPtr]['scope_opener']=== false{
  216.             return;
  217.         }
  218.  
  219.         // The opening brace needs to be one space away from the closing parenthesis.
  220.         $openBrace $tokens[$stackPtr]['scope_opener'];
  221.         $next      $phpcsFile->findNext(T_WHITESPACE($closeBracket + 1)$openBracetrue);
  222.         if ($next !== false{
  223.             // Probably comments in between tokens, so don't check.
  224.             return;
  225.         }
  226.  
  227.         if ($tokens[$openBrace]['line'$tokens[$closeBracket]['line']{
  228.             $length = -1;
  229.         else if ($openBrace === ($closeBracket + 1)) {
  230.             $length = 0;
  231.         else if ($openBrace === ($closeBracket + 2)
  232.             && $tokens[($closeBracket + 1)]['code'=== T_WHITESPACE
  233.         {
  234.             $length strlen($tokens[($closeBracket + 1)]['content']);
  235.         else {
  236.             // Confused, so don't check.
  237.             $length = 1;
  238.         }
  239.  
  240.         if ($length === 1{
  241.             return;
  242.         }
  243.  
  244.         $data = array($length);
  245.         $code 'SpaceBeforeOpenBrace';
  246.  
  247.         $error 'There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found ';
  248.         if ($length === -1{
  249.             $error .= 'newline';
  250.             $code   'NewlineBeforeOpenBrace';
  251.         else {
  252.             $error .= '%s spaces';
  253.         }
  254.  
  255.         $fix $phpcsFile->addFixableError($error($closeBracket + 1)$code$data);
  256.         if ($fix === true{
  257.             if ($length === 0{
  258.                 $phpcsFile->fixer->addContent($closeBracket' ');
  259.             else {
  260.                 $phpcsFile->fixer->replaceToken(($closeBracket + 1)' ');
  261.             }
  262.         }
  263.  
  264.     }//end process()
  265.  
  266.  
  267. }//end class

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