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

Source for file SideEffectsSniff.php

Documentation is available at SideEffectsSniff.php

  1. <?php
  2. /**
  3.  * Ensures a file declares new symbols and causes no other side effects, or executes logic with side effects, but not both.
  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\PSR1\Sniffs\Files;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class SideEffectsSniff 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(T_OPEN_TAG);
  28.  
  29.     }//end register()
  30.  
  31.  
  32.     /**
  33.      * Processes this sniff, when one of its tokens is encountered.
  34.      *
  35.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  36.      * @param int                         $stackPtr  The position of the current token in
  37.      *                                                the token stack.
  38.      *
  39.      * @return void 
  40.      */
  41.     public function process(File $phpcsFile$stackPtr)
  42.     {
  43.         $tokens $phpcsFile->getTokens();
  44.         $result $this->searchForConflict($phpcsFile0($phpcsFile->numTokens - 1)$tokens);
  45.  
  46.         if ($result['symbol'!== null && $result['effect'!== null{
  47.             $error 'A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line %s and the first side effect is on line %s.';
  48.             $data  = array(
  49.                       $tokens[$result['symbol']]['line'],
  50.                       $tokens[$result['effect']]['line'],
  51.                      );
  52.             $phpcsFile->addWarning($error0'FoundWithSymbols'$data);
  53.             $phpcsFile->recordMetric($stackPtr'Declarations and side effects mixed''yes');
  54.         else {
  55.             $phpcsFile->recordMetric($stackPtr'Declarations and side effects mixed''no');
  56.         }
  57.  
  58.         // Ignore the rest of the file.
  59.         return ($phpcsFile->numTokens + 1);
  60.  
  61.     }//end process()
  62.  
  63.  
  64.     /**
  65.      * Searches for symbol declarations and side effects.
  66.      *
  67.      * Returns the positions of both the first symbol declared and the first
  68.      * side effect in the file. A NULL value for either indicates nothing was
  69.      * found.
  70.      *
  71.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  72.      * @param int                         $start     The token to start searching from.
  73.      * @param int                         $end       The token to search to.
  74.      * @param array                       $tokens    The stack of tokens that make up
  75.      *                                                the file.
  76.      *
  77.      * @return array 
  78.      */
  79.     private function searchForConflict($phpcsFile$start$end$tokens)
  80.     {
  81.         $symbols = array(
  82.                     T_CLASS     => T_CLASS,
  83.                     T_INTERFACE => T_INTERFACE,
  84.                     T_TRAIT     => T_TRAIT,
  85.                     T_FUNCTION  => T_FUNCTION,
  86.                    );
  87.  
  88.         $conditions = array(
  89.                        T_IF     => T_IF,
  90.                        T_ELSE   => T_ELSE,
  91.                        T_ELSEIF => T_ELSEIF,
  92.                       );
  93.  
  94.         $firstSymbol = null;
  95.         $firstEffect = null;
  96.         for ($i $start$i <= $end$i++{
  97.             // Ignore whitespace and comments.
  98.             if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]=== true{
  99.                 continue;
  100.             }
  101.  
  102.             // Ignore PHP tags.
  103.             if ($tokens[$i]['code'=== T_OPEN_TAG
  104.                 || $tokens[$i]['code'=== T_CLOSE_TAG
  105.             {
  106.                 continue;
  107.             }
  108.  
  109.             // Ignore shebang.
  110.             if (substr($tokens[$i]['content']02=== '#!'{
  111.                 continue;
  112.             }
  113.  
  114.             // Ignore entire namespace, declare, const and use statements.
  115.             if ($tokens[$i]['code'=== T_NAMESPACE
  116.                 || $tokens[$i]['code'=== T_USE
  117.                 || $tokens[$i]['code'=== T_DECLARE
  118.                 || $tokens[$i]['code'=== T_CONST
  119.             {
  120.                 if (isset($tokens[$i]['scope_opener']=== true{
  121.                     $i $tokens[$i]['scope_closer'];
  122.                 else {
  123.                     $semicolon $phpcsFile->findNext(T_SEMICOLON($i + 1));
  124.                     if ($semicolon !== false{
  125.                         $i $semicolon;
  126.                     }
  127.                 }
  128.  
  129.                 continue;
  130.             }
  131.  
  132.             // Ignore function/class prefixes.
  133.             if (isset(Tokens::$methodPrefixes[$tokens[$i]['code']]=== true{
  134.                 continue;
  135.             }
  136.  
  137.             // Ignore anon classes.
  138.             if ($tokens[$i]['code'=== T_ANON_CLASS{
  139.                 $i $tokens[$i]['scope_closer'];
  140.                 continue;
  141.             }
  142.  
  143.             // Detect and skip over symbols.
  144.             if (isset($symbols[$tokens[$i]['code']]=== true
  145.                 && isset($tokens[$i]['scope_closer']=== true
  146.             {
  147.                 if ($firstSymbol === null{
  148.                     $firstSymbol $i;
  149.                 }
  150.  
  151.                 $i $tokens[$i]['scope_closer'];
  152.                 continue;
  153.             else if ($tokens[$i]['code'=== T_STRING
  154.                 && strtolower($tokens[$i]['content']=== 'define'
  155.             {
  156.                 $prev $phpcsFile->findPrevious(T_WHITESPACE($i - 1)nulltrue);
  157.                 if ($tokens[$prev]['code'!== T_OBJECT_OPERATOR{
  158.                     if ($firstSymbol === null{
  159.                         $firstSymbol $i;
  160.                     }
  161.  
  162.                     $semicolon $phpcsFile->findNext(T_SEMICOLON($i + 1));
  163.                     if ($semicolon !== false{
  164.                         $i $semicolon;
  165.                     }
  166.  
  167.                     continue;
  168.                 }
  169.             }//end if
  170.  
  171.             // Conditional statements are allowed in symbol files as long as the
  172.             // contents is only a symbol definition. So don't count these as effects
  173.             // in this case.
  174.             if (isset($conditions[$tokens[$i]['code']]=== true{
  175.                 if (isset($tokens[$i]['scope_opener']=== false{
  176.                     // Probably an "else if", so just ignore.
  177.                     continue;
  178.                 }
  179.  
  180.                 $result $this->searchForConflict(
  181.                     $phpcsFile,
  182.                     ($tokens[$i]['scope_opener'+ 1),
  183.                     ($tokens[$i]['scope_closer'- 1),
  184.                     $tokens
  185.                 );
  186.  
  187.                 if ($result['symbol'!== null{
  188.                     if ($firstSymbol === null{
  189.                         $firstSymbol $result['symbol'];
  190.                     }
  191.  
  192.                     if ($result['effect'!== null{
  193.                         // Found a conflict.
  194.                         $firstEffect $result['effect'];
  195.                         break;
  196.                     }
  197.                 }
  198.  
  199.                 if ($firstEffect === null{
  200.                     $firstEffect $result['effect'];
  201.                 }
  202.  
  203.                 $i $tokens[$i]['scope_closer'];
  204.                 continue;
  205.             }//end if
  206.  
  207.             if ($firstEffect === null{
  208.                 $firstEffect $i;
  209.             }
  210.  
  211.             if ($firstSymbol !== null{
  212.                 // We have a conflict we have to report, so no point continuing.
  213.                 break;
  214.             }
  215.         }//end for
  216.  
  217.         return array(
  218.                 'symbol' => $firstSymbol,
  219.                 'effect' => $firstEffect,
  220.                );
  221.  
  222.     }//end searchForConflict()
  223.  
  224.  
  225. }//end class

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