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

Source for file InlineControlStructureSniff.php

Documentation is available at InlineControlStructureSniff.php

  1. <?php
  2. /**
  3.  * Verifies that inline control statements are not present.
  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\Generic\Sniffs\ControlStructures;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class InlineControlStructureSniff 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.      * If true, an error will be thrown; otherwise a warning.
  31.      *
  32.      * @var boolean 
  33.      */
  34.     public $error = true;
  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_ELSE,
  47.                 T_ELSEIF,
  48.                 T_FOREACH,
  49.                 T_WHILE,
  50.                 T_DO,
  51.                 T_SWITCH,
  52.                 T_FOR,
  53.                );
  54.  
  55.     }//end register()
  56.  
  57.  
  58.     /**
  59.      * Processes this test, when one of its tokens is encountered.
  60.      *
  61.      * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  62.      * @param int                  $stackPtr  The position of the current token in the
  63.      *                                         stack passed in $tokens.
  64.      *
  65.      * @return void 
  66.      */
  67.     public function process(File $phpcsFile$stackPtr)
  68.     {
  69.         $tokens $phpcsFile->getTokens();
  70.  
  71.         if (isset($tokens[$stackPtr]['scope_opener']=== true{
  72.             $phpcsFile->recordMetric($stackPtr'Control structure defined inline''no');
  73.             return;
  74.         }
  75.  
  76.         // Ignore the ELSE in ELSE IF. We'll process the IF part later.
  77.         if ($tokens[$stackPtr]['code'=== T_ELSE{
  78.             $next $phpcsFile->findNext(T_WHITESPACE($stackPtr + 1)nulltrue);
  79.             if ($tokens[$next]['code'=== T_IF{
  80.                 return;
  81.             }
  82.         }
  83.  
  84.         if ($tokens[$stackPtr]['code'=== T_WHILE{
  85.             // This could be from a DO WHILE, which doesn't have an opening brace.
  86.             $lastContent $phpcsFile->findPrevious(T_WHITESPACE($stackPtr - 1)nulltrue);
  87.             if ($tokens[$lastContent]['code'=== T_CLOSE_CURLY_BRACKET{
  88.                 $brace $tokens[$lastContent];
  89.                 if (isset($brace['scope_condition']=== true{
  90.                     $condition $tokens[$brace['scope_condition']];
  91.                     if ($condition['code'=== T_DO{
  92.                         return;
  93.                     }
  94.                 }
  95.             }
  96.  
  97.             // In Javascript DO WHILE loops without curly braces are legal. This
  98.             // is only valid if a single statement is present between the DO and
  99.             // the WHILE. We can detect this by checking only a single semicolon
  100.             // is present between them.
  101.             if ($phpcsFile->tokenizerType === 'JS'{
  102.                 $lastDo        $phpcsFile->findPrevious(T_DO($stackPtr - 1));
  103.                 $lastSemicolon $phpcsFile->findPrevious(T_SEMICOLON($stackPtr - 1));
  104.                 if ($lastDo !== false && $lastSemicolon !== false && $lastDo $lastSemicolon{
  105.                     $precedingSemicolon $phpcsFile->findPrevious(T_SEMICOLON($lastSemicolon - 1));
  106.                     if ($precedingSemicolon === false || $precedingSemicolon $lastDo{
  107.                         return;
  108.                     }
  109.                 }
  110.             }
  111.         }//end if
  112.  
  113.         // This is a control structure without an opening brace,
  114.         // so it is an inline statement.
  115.         if ($this->error === true{
  116.             $fix $phpcsFile->addFixableError('Inline control structures are not allowed'$stackPtr'NotAllowed');
  117.         else {
  118.             $fix $phpcsFile->addFixableWarning('Inline control structures are discouraged'$stackPtr'Discouraged');
  119.         }
  120.  
  121.         $phpcsFile->recordMetric($stackPtr'Control structure defined inline''yes');
  122.  
  123.         // Stop here if we are not fixing the error.
  124.         if ($fix !== true{
  125.             return;
  126.         }
  127.  
  128.         $phpcsFile->fixer->beginChangeset();
  129.         if (isset($tokens[$stackPtr]['parenthesis_closer']=== true{
  130.             $closer $tokens[$stackPtr]['parenthesis_closer'];
  131.         else {
  132.             $closer $stackPtr;
  133.         }
  134.  
  135.         if ($tokens[($closer + 1)]['code'=== T_WHITESPACE
  136.             || $tokens[($closer + 1)]['code'=== T_SEMICOLON
  137.         {
  138.             $phpcsFile->fixer->addContent($closer' {');
  139.         else {
  140.             $phpcsFile->fixer->addContent($closer' { ');
  141.         }
  142.  
  143.         $fixableScopeOpeners $this->register();
  144.  
  145.         $lastNonEmpty $closer;
  146.         for ($end ($closer + 1)$end $phpcsFile->numTokens; $end++{
  147.             if ($tokens[$end]['code'=== T_SEMICOLON{
  148.                 break;
  149.             }
  150.  
  151.             if ($tokens[$end]['code'=== T_CLOSE_TAG{
  152.                 $end $lastNonEmpty;
  153.                 break;
  154.             }
  155.  
  156.             if (in_array($tokens[$end]['code']$fixableScopeOpeners=== true
  157.                 && isset($tokens[$end]['scope_opener']=== false
  158.             {
  159.                 // The best way to fix nested inline scopes is middle-out.
  160.                 // So skip this one. It will be detected and fixed on a future loop.
  161.                 $phpcsFile->fixer->rollbackChangeset();
  162.                 return;
  163.             }
  164.  
  165.             if (isset($tokens[$end]['scope_opener']=== true{
  166.                 $type $tokens[$end]['code'];
  167.                 $end  $tokens[$end]['scope_closer'];
  168.                 if ($type === T_DO || $type === T_IF || $type === T_ELSEIF{
  169.                     $next $phpcsFile->findNext(Tokens::$emptyTokens($end + 1)nulltrue);
  170.                     if ($next === false{
  171.                         break;
  172.                     }
  173.  
  174.                     $nextType $tokens[$next]['code'];
  175.  
  176.                     // Let additional conditions loop and find their ending.
  177.                     if (($type === T_IF
  178.                         || $type === T_ELSEIF)
  179.                         && ($nextType === T_ELSEIF
  180.                         || $nextType === T_ELSE)
  181.                     {
  182.                         continue;
  183.                     }
  184.  
  185.                     // Account for DO... WHILE conditions.
  186.                     if ($type === T_DO && $nextType === T_WHILE{
  187.                         $end $phpcsFile->findNext(T_SEMICOLON($next + 1));
  188.                     }
  189.                 }//end if
  190.  
  191.                 break;
  192.             }//end if
  193.  
  194.             if ($tokens[$end]['code'!== T_WHITESPACE{
  195.                 $lastNonEmpty $end;
  196.             }
  197.         }//end for
  198.  
  199.         $next $phpcsFile->findNext(T_WHITESPACE($closer + 1)($end + 1)true);
  200.  
  201.         // Account for a comment on the end of the line.
  202.         for ($endLine $end$endLine $phpcsFile->numTokens; $endLine++{
  203.             if (isset($tokens[($endLine + 1)]=== false
  204.                 || $tokens[$endLine]['line'!== $tokens[($endLine + 1)]['line']
  205.             {
  206.                 break;
  207.             }
  208.         }
  209.  
  210.         if ($tokens[$endLine]['code'!== T_COMMENT{
  211.             $endLine $end;
  212.         }
  213.  
  214.         if ($next !== $end{
  215.             if ($endLine !== $end{
  216.                 $phpcsFile->fixer->addContent($endLine'}');
  217.             else {
  218.                 if ($tokens[$end]['code'!== T_SEMICOLON
  219.                     && $tokens[$end]['code'!== T_CLOSE_CURLY_BRACKET
  220.                 {
  221.                     $phpcsFile->fixer->addContent($end';');
  222.                 }
  223.  
  224.                 $phpcsFile->fixer->addContent($end' }');
  225.             }
  226.         else {
  227.             if ($endLine !== $end{
  228.                 $phpcsFile->fixer->replaceToken($end'');
  229.                 $phpcsFile->fixer->addNewlineBefore($endLine);
  230.                 $phpcsFile->fixer->addContent($endLine'}');
  231.             else {
  232.                 $phpcsFile->fixer->replaceToken($end'}');
  233.             }
  234.         }//end if
  235.  
  236.         $phpcsFile->fixer->endChangeset();
  237.  
  238.     }//end process()
  239.  
  240.  
  241. }//end class

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