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_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 ($tokens[$i]['line'!== $prevLine{
  112.                 if ($tokens[$i]['line'=== $tokens[$closeBracket]['line']{
  113.                     $next $phpcsFile->findNext(T_WHITESPACE$inulltrue);
  114.                     if ($next !== $closeBracket{
  115.                         // Closing bracket is on the same line as a condition.
  116.                         $error 'Closing parenthesis of a multi-line IF statement must be on a new line';
  117.                         $fix   $phpcsFile->addFixableError($error$closeBracket'CloseBracketNewLine');
  118.                         if ($fix === true{
  119.                             // Account for a comment at the end of the line.
  120.                             $next $phpcsFile->findNext(T_WHITESPACE($closeBracket + 1)nulltrue);
  121.                             if ($tokens[$next]['code'!== T_COMMENT{
  122.                                 $phpcsFile->fixer->addNewlineBefore($closeBracket);
  123.                             else {
  124.                                 $next $phpcsFile->findNext(Tokens::$emptyTokens($next + 1)nulltrue);
  125.                                 $phpcsFile->fixer->beginChangeset();
  126.                                 $phpcsFile->fixer->replaceToken($closeBracket'');
  127.                                 $phpcsFile->fixer->addContentBefore($next')');
  128.                                 $phpcsFile->fixer->endChangeset();
  129.                             }
  130.                         }
  131.  
  132.                         $expectedIndent ($statementIndent $this->indent);
  133.                     else {
  134.                         // Closing brace needs to be indented to the same level
  135.                         // as the statement.
  136.                         $expectedIndent $statementIndent;
  137.                     }//end if
  138.                 else {
  139.                     $expectedIndent ($statementIndent $this->indent);
  140.                 }//end if
  141.  
  142.                 if ($tokens[$i]['code'=== T_COMMENT{
  143.                     $prevLine $tokens[$i]['line'];
  144.                     continue;
  145.                 }
  146.  
  147.                 // We changed lines, so this should be a whitespace indent token.
  148.                 if ($tokens[$i]['code'!== T_WHITESPACE{
  149.                     $foundIndent = 0;
  150.                 else {
  151.                     $foundIndent strlen($tokens[$i]['content']);
  152.                 }
  153.  
  154.                 if ($expectedIndent !== $foundIndent{
  155.                     $error 'Multi-line IF statement not indented correctly; expected %s spaces but found %s';
  156.                     $data  = array(
  157.                               $expectedIndent,
  158.                               $foundIndent,
  159.                              );
  160.  
  161.                     $fix $phpcsFile->addFixableError($error$i'Alignment'$data);
  162.                     if ($fix === true{
  163.                         $spaces str_repeat(' '$expectedIndent);
  164.                         if ($foundIndent === 0{
  165.                             $phpcsFile->fixer->addContentBefore($i$spaces);
  166.                         else {
  167.                             $phpcsFile->fixer->replaceToken($i$spaces);
  168.                         }
  169.                     }
  170.                 }
  171.  
  172.                 $next $phpcsFile->findNext(Tokens::$emptyTokens$inulltrue);
  173.                 if ($next !== $closeBracket{
  174.                     if (isset(Tokens::$booleanOperators[$tokens[$next]['code']]=== false{
  175.                         $error 'Each line in a multi-line IF statement must begin with a boolean operator';
  176.                         $fix   $phpcsFile->addFixableError($error$i'StartWithBoolean');
  177.                         if ($fix === true{
  178.                             $prev $phpcsFile->findPrevious(Tokens::$emptyTokens($i - 1)$openBrackettrue);
  179.                             if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]=== true{
  180.                                 $phpcsFile->fixer->beginChangeset();
  181.                                 $phpcsFile->fixer->replaceToken($prev'');
  182.                                 $phpcsFile->fixer->addContentBefore($next$tokens[$prev]['content'].' ');
  183.                                 $phpcsFile->fixer->endChangeset();
  184.                             else {
  185.                                 for ($x ($prev + 1)$x $next$x++{
  186.                                     $phpcsFile->fixer->replaceToken($x'');
  187.                                 }
  188.                             }
  189.                         }
  190.                     }
  191.                 }//end if
  192.  
  193.                 $prevLine $tokens[$i]['line'];
  194.             }//end if
  195.  
  196.             if ($tokens[$i]['code'=== T_STRING{
  197.                 $next $phpcsFile->findNext(T_WHITESPACE($i + 1)nulltrue);
  198.                 if ($tokens[$next]['code'=== T_OPEN_PARENTHESIS{
  199.                     // This is a function call, so skip to the end as they
  200.                     // have their own indentation rules.
  201.                     $i        $tokens[$next]['parenthesis_closer'];
  202.                     $prevLine $tokens[$i]['line'];
  203.                     continue;
  204.                 }
  205.             }
  206.         }//end for
  207.  
  208.         // From here on, we are checking the spacing of the opening and closing
  209.         // braces. If this IF statement does not use braces, we end here.
  210.         if (isset($tokens[$stackPtr]['scope_opener']=== false{
  211.             return;
  212.         }
  213.  
  214.         // The opening brace needs to be one space away from the closing parenthesis.
  215.         $openBrace $tokens[$stackPtr]['scope_opener'];
  216.         $next      $phpcsFile->findNext(T_WHITESPACE($closeBracket + 1)$openBracetrue);
  217.         if ($next !== false{
  218.             // Probably comments in between tokens, so don't check.
  219.             return;
  220.         }
  221.  
  222.         if ($tokens[$openBrace]['line'$tokens[$closeBracket]['line']{
  223.             $length = -1;
  224.         else if ($openBrace === ($closeBracket + 1)) {
  225.             $length = 0;
  226.         else if ($openBrace === ($closeBracket + 2)
  227.             && $tokens[($closeBracket + 1)]['code'=== T_WHITESPACE
  228.         {
  229.             $length strlen($tokens[($closeBracket + 1)]['content']);
  230.         else {
  231.             // Confused, so don't check.
  232.             $length = 1;
  233.         }
  234.  
  235.         if ($length === 1{
  236.             return;
  237.         }
  238.  
  239.         $data = array($length);
  240.         $code 'SpaceBeforeOpenBrace';
  241.  
  242.         $error 'There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found ';
  243.         if ($length === -1{
  244.             $error .= 'newline';
  245.             $code   'NewlineBeforeOpenBrace';
  246.         else {
  247.             $error .= '%s spaces';
  248.         }
  249.  
  250.         $fix $phpcsFile->addFixableError($error($closeBracket + 1)$code$data);
  251.         if ($fix === true{
  252.             if ($length === 0{
  253.                 $phpcsFile->fixer->addContent($closeBracket' ');
  254.             else {
  255.                 $phpcsFile->fixer->replaceToken(($closeBracket + 1)' ');
  256.             }
  257.         }
  258.  
  259.     }//end process()
  260.  
  261.  
  262. }//end class

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