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\Files\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(Tokens::$emptyTokens($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 || $type === T_TRY{
  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.  
  190.                     // Account for TRY... CATCH statements.
  191.                     if ($type === T_TRY && $nextType === T_CATCH{
  192.                         $end $tokens[$next]['scope_closer'];
  193.                     }
  194.                 }//end if
  195.  
  196.                 if ($tokens[$end]['code'!== T_END_HEREDOC
  197.                     && $tokens[$end]['code'!== T_END_NOWDOC
  198.                 {
  199.                     break;
  200.                 }
  201.             }//end if
  202.  
  203.             if (isset($tokens[$end]['parenthesis_closer']=== true{
  204.                 $end          $tokens[$end]['parenthesis_closer'];
  205.                 $lastNonEmpty $end;
  206.                 continue;
  207.             }
  208.  
  209.             if ($tokens[$end]['code'!== T_WHITESPACE{
  210.                 $lastNonEmpty $end;
  211.             }
  212.         }//end for
  213.  
  214.         if ($end === $phpcsFile->numTokens{
  215.             $end $lastNonEmpty;
  216.         }
  217.  
  218.         $next $phpcsFile->findNext(Tokens::$emptyTokens($end + 1)nulltrue);
  219.  
  220.         if ($next === false || $tokens[$next]['line'!== $tokens[$end]['line']{
  221.             // Looks for completely empty statements.
  222.             $next $phpcsFile->findNext(T_WHITESPACE($closer + 1)($end + 1)true);
  223.  
  224.             // Account for a comment on the end of the line.
  225.             for ($endLine $end$endLine $phpcsFile->numTokens; $endLine++{
  226.                 if (isset($tokens[($endLine + 1)]=== false
  227.                     || $tokens[$endLine]['line'!== $tokens[($endLine + 1)]['line']
  228.                 {
  229.                     break;
  230.                 }
  231.             }
  232.  
  233.             if ($tokens[$endLine]['code'!== T_COMMENT{
  234.                 $endLine $end;
  235.             }
  236.         else {
  237.             $next    ($end + 1);
  238.             $endLine $end;
  239.         }
  240.  
  241.         if ($next !== $end{
  242.             if ($endLine !== $end{
  243.                 $endToken     $endLine;
  244.                 $addedContent '';
  245.             else {
  246.                 $endToken     $end;
  247.                 $addedContent $phpcsFile->eolChar;
  248.  
  249.                 if ($tokens[$end]['code'!== T_SEMICOLON
  250.                     && $tokens[$end]['code'!== T_CLOSE_CURLY_BRACKET
  251.                 {
  252.                     $phpcsFile->fixer->addContent($end'; ');
  253.                 }
  254.             }
  255.  
  256.             $next $phpcsFile->findNext(T_WHITESPACE($endToken + 1)nulltrue);
  257.             if ($next !== false
  258.                 && ($tokens[$next]['code'=== T_ELSE
  259.                 || $tokens[$next]['code'=== T_ELSEIF)
  260.             {
  261.                 $phpcsFile->fixer->addContentBefore($next'} ');
  262.             else {
  263.                 $indent '';
  264.                 for ($first $stackPtr$first > 0; $first--{
  265.                     if ($first === 1
  266.                         || $tokens[($first - 1)]['line'!== $tokens[$first]['line']
  267.                     {
  268.                         break;
  269.                     }
  270.                 }
  271.  
  272.                 if ($tokens[$first]['code'=== T_WHITESPACE{
  273.                     $indent $tokens[$first]['content'];
  274.                 else if ($tokens[$first]['code'=== T_INLINE_HTML
  275.                     || $tokens[$first]['code'=== T_OPEN_TAG
  276.                 {
  277.                     $addedContent '';
  278.                 }
  279.  
  280.                 $addedContent .= $indent.'}';
  281.                 if ($next !== false && $tokens[$endToken]['code'=== T_COMMENT{
  282.                     $addedContent .= $phpcsFile->eolChar;
  283.                 }
  284.  
  285.                 $phpcsFile->fixer->addContent($endToken$addedContent);
  286.             }//end if
  287.         else {
  288.             if ($endLine !== $end{
  289.                 $phpcsFile->fixer->replaceToken($end'');
  290.                 $phpcsFile->fixer->addNewlineBefore($endLine);
  291.                 $phpcsFile->fixer->addContent($endLine'}');
  292.             else {
  293.                 $phpcsFile->fixer->replaceToken($end'}');
  294.             }
  295.         }//end if
  296.  
  297.         $phpcsFile->fixer->endChangeset();
  298.  
  299.     }//end process()
  300.  
  301.  
  302. }//end class

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