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

Source for file NonExecutableCodeSniff.php

Documentation is available at NonExecutableCodeSniff.php

  1. <?php
  2. /**
  3.  * Warns about code that can never been executed.
  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\Squiz\Sniffs\PHP;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class NonExecutableCodeSniff implements Sniff
  17. {
  18.  
  19.  
  20.     /**
  21.      * Returns an array of tokens this test wants to listen for.
  22.      *
  23.      * @return array 
  24.      */
  25.     public function register()
  26.     {
  27.         return array(
  28.                 T_BREAK,
  29.                 T_CONTINUE,
  30.                 T_RETURN,
  31.                 T_THROW,
  32.                 T_EXIT,
  33.                );
  34.  
  35.     }//end register()
  36.  
  37.  
  38.     /**
  39.      * Processes this test, when one of its tokens is encountered.
  40.      *
  41.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  42.      * @param int                         $stackPtr  The position of the current token in
  43.      *                                                the stack passed in $tokens.
  44.      *
  45.      * @return void 
  46.      */
  47.     public function process(File $phpcsFile$stackPtr)
  48.     {
  49.         $tokens $phpcsFile->getTokens();
  50.  
  51.         // If this token is preceded with an "or", it only relates to one line
  52.         // and should be ignored. For example: fopen() or die().
  53.         $prev $phpcsFile->findPrevious(Tokens::$emptyTokens($stackPtr - 1)nulltrue);
  54.         if ($tokens[$prev]['code'=== T_LOGICAL_OR || $tokens[$prev]['code'=== T_BOOLEAN_OR{
  55.             return;
  56.         }
  57.  
  58.         // Check if this token is actually part of a one-line IF or ELSE statement.
  59.         for ($i ($stackPtr - 1)$i > 0; $i--{
  60.             if ($tokens[$i]['code'=== T_CLOSE_PARENTHESIS{
  61.                 $i $tokens[$i]['parenthesis_opener'];
  62.                 continue;
  63.             else if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]=== true{
  64.                 continue;
  65.             }
  66.  
  67.             break;
  68.         }
  69.  
  70.         if ($tokens[$i]['code'=== T_IF
  71.             || $tokens[$i]['code'=== T_ELSE
  72.             || $tokens[$i]['code'=== T_ELSEIF
  73.         {
  74.             return;
  75.         }
  76.  
  77.         if ($tokens[$stackPtr]['code'=== T_RETURN{
  78.             $next $phpcsFile->findNext(T_WHITESPACE($stackPtr + 1)nulltrue);
  79.             if ($tokens[$next]['code'=== T_SEMICOLON{
  80.                 $next $phpcsFile->findNext(T_WHITESPACE($next + 1)nulltrue);
  81.                 if ($tokens[$next]['code'=== T_CLOSE_CURLY_BRACKET{
  82.                     // If this is the closing brace of a function
  83.                     // then this return statement doesn't return anything
  84.                     // and is not required anyway.
  85.                     $owner $tokens[$next]['scope_condition'];
  86.                     if ($tokens[$owner]['code'=== T_FUNCTION{
  87.                         $warning 'Empty return statement not required here';
  88.                         $phpcsFile->addWarning($warning$stackPtr'ReturnNotRequired');
  89.                         return;
  90.                     }
  91.                 }
  92.             }
  93.         }
  94.  
  95.         if (isset($tokens[$stackPtr]['scope_opener']=== true{
  96.             $owner $tokens[$stackPtr]['scope_condition'];
  97.             if ($tokens[$owner]['code'=== T_CASE || $tokens[$owner]['code'=== T_DEFAULT{
  98.                 // This token closes the scope of a CASE or DEFAULT statement
  99.                 // so any code between this statement and the next CASE, DEFAULT or
  100.                 // end of SWITCH token will not be executable.
  101.                 $end  $phpcsFile->findEndOfStatement($stackPtr);
  102.                 $next $phpcsFile->findNext(
  103.                     array(
  104.                      T_CASE,
  105.                      T_DEFAULT,
  106.                      T_CLOSE_CURLY_BRACKET,
  107.                     ),
  108.                     ($end + 1)
  109.                 );
  110.  
  111.                 if ($next !== false{
  112.                     $lastLine $tokens[$end]['line'];
  113.                     for ($i ($stackPtr + 1)$i $next$i++{
  114.                         if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]=== true{
  115.                             continue;
  116.                         }
  117.  
  118.                         $line $tokens[$i]['line'];
  119.                         if ($line $lastLine{
  120.                             $type    substr($tokens[$stackPtr]['type']2);
  121.                             $warning 'Code after %s statement cannot be executed';
  122.                             $data    = array($type);
  123.                             $phpcsFile->addWarning($warning$i'Unreachable'$data);
  124.                             $lastLine $line;
  125.                         }
  126.                     }
  127.                 }//end if
  128.  
  129.                 // That's all we have to check for these types of statements.
  130.                 return;
  131.             }//end if
  132.         }//end if
  133.  
  134.         // This token may be part of an inline condition.
  135.         // If we find a closing parenthesis that belongs to a condition
  136.         // we should ignore this token.
  137.         $prev $phpcsFile->findPrevious(Tokens::$emptyTokens($stackPtr - 1)nulltrue);
  138.         if (isset($tokens[$prev]['parenthesis_owner']=== true{
  139.             $owner  $tokens[$prev]['parenthesis_owner'];
  140.             $ignore = array(
  141.                        T_IF     => true,
  142.                        T_ELSE   => true,
  143.                        T_ELSEIF => true,
  144.                       );
  145.             if (isset($ignore[$tokens[$owner]['code']]=== true{
  146.                 return;
  147.             }
  148.         }
  149.  
  150.         $ourConditions array_keys($tokens[$stackPtr]['conditions']);
  151.  
  152.         if (empty($ourConditions=== false{
  153.             $condition array_pop($ourConditions);
  154.  
  155.             if (isset($tokens[$condition]['scope_closer']=== false{
  156.                 return;
  157.             }
  158.  
  159.             // Special case for BREAK statements sitting directly inside SWITCH
  160.             // statements. If we get to this point, we know the BREAK is not being
  161.             // used to close a CASE statement, so it is most likely non-executable
  162.             // code itself (as is the case when you put return; break; at the end of
  163.             // a case). So we need to ignore this token.
  164.             if ($tokens[$condition]['code'=== T_SWITCH
  165.                 && $tokens[$stackPtr]['code'=== T_BREAK
  166.             {
  167.                 return;
  168.             }
  169.  
  170.             $closer $tokens[$condition]['scope_closer'];
  171.  
  172.             // If the closer for our condition is shared with other openers,
  173.             // we will need to throw errors from this token to the next
  174.             // shared opener (if there is one), not to the scope closer.
  175.             $nextOpener = null;
  176.             for ($i ($stackPtr + 1)$i $closer$i++{
  177.                 if (isset($tokens[$i]['scope_closer']=== true{
  178.                     if ($tokens[$i]['scope_closer'=== $closer{
  179.                         // We found an opener that shares the same
  180.                         // closing token as us.
  181.                         $nextOpener $i;
  182.                         break;
  183.                     }
  184.                 }
  185.             }//end for
  186.  
  187.             if ($nextOpener === null{
  188.                 $end $closer;
  189.             else {
  190.                 $end ($nextOpener - 1);
  191.             }
  192.         else {
  193.             // This token is in the global scope.
  194.             if ($tokens[$stackPtr]['code'=== T_BREAK{
  195.                 return;
  196.             }
  197.  
  198.             // Throw an error for all lines until the end of the file.
  199.             $end ($phpcsFile->numTokens - 1);
  200.         }//end if
  201.  
  202.         // Find the semicolon that ends this statement, skipping
  203.         // nested statements like FOR loops and closures.
  204.         for ($start ($stackPtr + 1)$start $phpcsFile->numTokens; $start++{
  205.             if ($start === $end{
  206.                 break;
  207.             }
  208.  
  209.             if ($tokens[$start]['code'=== T_OPEN_PARENTHESIS{
  210.                 $start $tokens[$start]['parenthesis_closer'];
  211.                 continue;
  212.             }
  213.  
  214.             if ($tokens[$start]['code'=== T_OPEN_CURLY_BRACKET{
  215.                 $start $tokens[$start]['bracket_closer'];
  216.                 continue;
  217.             }
  218.  
  219.             if ($tokens[$start]['code'=== T_SEMICOLON{
  220.                 break;
  221.             }
  222.         }//end for
  223.  
  224.         $lastLine $tokens[$start]['line'];
  225.         for ($i ($start + 1)$i $end$i++{
  226.             if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]=== true
  227.                 || isset(Tokens::$bracketTokens[$tokens[$i]['code']]=== true
  228.             {
  229.                 continue;
  230.             }
  231.  
  232.             // Skip whole functions and classes/interfaces because they are not
  233.             // technically executed code, but rather declarations that may be used.
  234.             if ($tokens[$i]['code'=== T_FUNCTION
  235.                 || $tokens[$i]['code'=== T_CLASS
  236.                 || $tokens[$i]['code'=== T_INTERFACE
  237.             {
  238.                 $i $tokens[$i]['scope_closer'];
  239.                 continue;
  240.             }
  241.  
  242.             $line $tokens[$i]['line'];
  243.             if ($line $lastLine{
  244.                 $type    substr($tokens[$stackPtr]['type']2);
  245.                 $warning 'Code after %s statement cannot be executed';
  246.                 $data    = array($type);
  247.                 $phpcsFile->addWarning($warning$i'Unreachable'$data);
  248.                 $lastLine $line;
  249.             }
  250.         }//end for
  251.  
  252.     }//end process()
  253.  
  254.  
  255. }//end class

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