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

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