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

Source for file OperatorBracketSniff.php

Documentation is available at OperatorBracketSniff.php

  1. <?php
  2. /**
  3.  * Tests that all arithmetic operations are bracketed.
  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\Formatting;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class OperatorBracketSniff 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.     /**
  31.      * Returns an array of tokens this test wants to listen for.
  32.      *
  33.      * @return array 
  34.      */
  35.     public function register()
  36.     {
  37.         return Tokens::$operators;
  38.  
  39.     }//end register()
  40.  
  41.  
  42.     /**
  43.      * Processes this test, when one of its tokens is encountered.
  44.      *
  45.      * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  46.      * @param int                  $stackPtr  The position of the current token in the
  47.      *                                         stack passed in $tokens.
  48.      *
  49.      * @return void 
  50.      */
  51.     public function process(File $phpcsFile$stackPtr)
  52.     {
  53.         $tokens $phpcsFile->getTokens();
  54.  
  55.         if ($phpcsFile->tokenizerType === 'JS' && $tokens[$stackPtr]['code'=== T_PLUS{
  56.             // JavaScript uses the plus operator for string concatenation as well
  57.             // so we cannot accurately determine if it is a string concat or addition.
  58.             // So just ignore it.
  59.             return;
  60.         }
  61.  
  62.         // If the & is a reference, then we don't want to check for brackets.
  63.         if ($tokens[$stackPtr]['code'=== T_BITWISE_AND && $phpcsFile->isReference($stackPtr=== true{
  64.             return;
  65.         }
  66.  
  67.         // There is one instance where brackets aren't needed, which involves
  68.         // the minus sign being used to assign a negative number to a variable.
  69.         if ($tokens[$stackPtr]['code'=== T_MINUS{
  70.             // Check to see if we are trying to return -n.
  71.             $prev $phpcsFile->findPrevious(Tokens::$emptyTokens($stackPtr - 1)nulltrue);
  72.             if ($tokens[$prev]['code'=== T_RETURN{
  73.                 return;
  74.             }
  75.  
  76.             $number $phpcsFile->findNext(T_WHITESPACE($stackPtr + 1)nulltrue);
  77.             if ($tokens[$number]['code'=== T_LNUMBER || $tokens[$number]['code'=== T_DNUMBER{
  78.                 $previous $phpcsFile->findPrevious(T_WHITESPACE($stackPtr - 1)nulltrue);
  79.                 if ($previous !== false{
  80.                     $isAssignment in_array($tokens[$previous]['code']Tokens::$assignmentTokens);
  81.                     $isEquality   = in_array($tokens[$previous]['code']Tokens::$equalityTokens);
  82.                     $isComparison = in_array($tokens[$previous]['code']Tokens::$comparisonTokens);
  83.                     if ($isAssignment === true || $isEquality === true || $isComparison === true{
  84.                         // This is a negative assignment or comparison.
  85.                         // We need to check that the minus and the number are
  86.                         // adjacent.
  87.                         if (($number $stackPtr!== 1{
  88.                             $error 'No space allowed between minus sign and number';
  89.                             $phpcsFile->addError($error$stackPtr'SpacingAfterMinus');
  90.                         }
  91.  
  92.                         return;
  93.                     }
  94.                 }
  95.             }
  96.         }//end if
  97.  
  98.         $previousToken $phpcsFile->findPrevious(T_WHITESPACE($stackPtr - 1)nulltruenulltrue);
  99.         if ($previousToken !== false{
  100.             // A list of tokens that indicate that the token is not
  101.             // part of an arithmetic operation.
  102.             $invalidTokens = array(
  103.                               T_COMMA,
  104.                               T_COLON,
  105.                               T_OPEN_PARENTHESIS,
  106.                               T_OPEN_SQUARE_BRACKET,
  107.                               T_OPEN_SHORT_ARRAY,
  108.                               T_CASE,
  109.                              );
  110.  
  111.             if (in_array($tokens[$previousToken]['code']$invalidTokens=== true{
  112.                 return;
  113.             }
  114.         }
  115.  
  116.         // Tokens that are allowed inside a bracketed operation.
  117.         $allowed = array(
  118.                     T_VARIABLE,
  119.                     T_LNUMBER,
  120.                     T_DNUMBER,
  121.                     T_STRING,
  122.                     T_WHITESPACE,
  123.                     T_THIS,
  124.                     T_SELF,
  125.                     T_OBJECT_OPERATOR,
  126.                     T_DOUBLE_COLON,
  127.                     T_OPEN_SQUARE_BRACKET,
  128.                     T_CLOSE_SQUARE_BRACKET,
  129.                     T_MODULUS,
  130.                     T_NONE,
  131.                    );
  132.  
  133.         $allowed += Tokens::$operators;
  134.  
  135.         $lastBracket = false;
  136.         if (isset($tokens[$stackPtr]['nested_parenthesis']=== true{
  137.             $parenthesis array_reverse($tokens[$stackPtr]['nested_parenthesis']true);
  138.             foreach ($parenthesis as $bracket => $endBracket{
  139.                 $prevToken $phpcsFile->findPrevious(T_WHITESPACE($bracket - 1)nulltrue);
  140.                 $prevCode  $tokens[$prevToken]['code'];
  141.  
  142.                 if ($prevCode === T_ISSET{
  143.                     // This operation is inside an isset() call, but has
  144.                     // no bracket of it's own.
  145.                     break;
  146.                 }
  147.  
  148.                 if ($prevCode === T_STRING || $prevCode === T_SWITCH{
  149.                     // We allow simple operations to not be bracketed.
  150.                     // For example, ceil($one / $two).
  151.                     for ($prev ($stackPtr - 1)$prev $bracket$prev--{
  152.                         if (in_array($tokens[$prev]['code']$allowed=== true{
  153.                             continue;
  154.                         }
  155.  
  156.                         if ($tokens[$prev]['code'=== T_CLOSE_PARENTHESIS{
  157.                             $prev $tokens[$prev]['parenthesis_opener'];
  158.                         else {
  159.                             break;
  160.                         }
  161.                     }
  162.  
  163.                     if ($prev !== $bracket{
  164.                         break;
  165.                     }
  166.  
  167.                     for ($next ($stackPtr + 1)$next $endBracket$next++{
  168.                         if (in_array($tokens[$next]['code']$allowed=== true{
  169.                             continue;
  170.                         }
  171.  
  172.                         if ($tokens[$next]['code'=== T_OPEN_PARENTHESIS{
  173.                             $next $tokens[$next]['parenthesis_closer'];
  174.                         else {
  175.                             break;
  176.                         }
  177.                     }
  178.  
  179.                     if ($next !== $endBracket{
  180.                         break;
  181.                     }
  182.                 }//end if
  183.  
  184.                 if (in_array($prevCodeTokens::$scopeOpeners=== true{
  185.                     // This operation is inside a control structure like FOREACH
  186.                     // or IF, but has no bracket of it's own.
  187.                     // The only control structure allowed to do this is SWITCH.
  188.                     if ($prevCode !== T_SWITCH{
  189.                         break;
  190.                     }
  191.                 }
  192.  
  193.                 if ($prevCode === T_OPEN_PARENTHESIS{
  194.                     // These are two open parenthesis in a row. If the current
  195.                     // one doesn't enclose the operator, go to the previous one.
  196.                     if ($endBracket $stackPtr{
  197.                         continue;
  198.                     }
  199.                 }
  200.  
  201.                 $lastBracket $bracket;
  202.                 break;
  203.             }//end foreach
  204.         }//end if
  205.  
  206.         if ($lastBracket === false{
  207.             // It is not in a bracketed statement at all.
  208.             $this->addMissingBracketsError($phpcsFile$stackPtr);
  209.             return;
  210.         else if ($tokens[$lastBracket]['parenthesis_closer'$stackPtr{
  211.             // There are a set of brackets in front of it that don't include it.
  212.             $this->addMissingBracketsError($phpcsFile$stackPtr);
  213.             return;
  214.         else {
  215.             // We are enclosed in a set of bracket, so the last thing to
  216.             // check is that we are not also enclosed in square brackets
  217.             // like this: ($array[$index + 1]), which is invalid.
  218.             $brackets = array(
  219.                          T_OPEN_SQUARE_BRACKET,
  220.                          T_CLOSE_SQUARE_BRACKET,
  221.                         );
  222.  
  223.             $squareBracket $phpcsFile->findPrevious($brackets($stackPtr - 1)$lastBracket);
  224.             if ($squareBracket !== false && $tokens[$squareBracket]['code'=== T_OPEN_SQUARE_BRACKET{
  225.                 $closeSquareBracket $phpcsFile->findNext($brackets($stackPtr + 1));
  226.                 if ($closeSquareBracket !== false && $tokens[$closeSquareBracket]['code'=== T_CLOSE_SQUARE_BRACKET{
  227.                     $this->addMissingBracketsError($phpcsFile$stackPtr);
  228.                 }
  229.             }
  230.  
  231.             return;
  232.         }//end if
  233.  
  234.         $lastAssignment $phpcsFile->findPrevious(Tokens::$assignmentTokens$stackPtrnullfalsenulltrue);
  235.         if ($lastAssignment !== false && $lastAssignment $lastBracket{
  236.             $this->addMissingBracketsError($phpcsFile$stackPtr);
  237.         }
  238.  
  239.     }//end process()
  240.  
  241.  
  242.     /**
  243.      * Add and fix the missing brackets error.
  244.      *
  245.      * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  246.      * @param int                  $stackPtr  The position of the current token in the
  247.      *                                         stack passed in $tokens.
  248.      *
  249.      * @return void 
  250.      */
  251.     public function addMissingBracketsError($phpcsFile$stackPtr)
  252.     {
  253.         $error 'Arithmetic operation must be bracketed';
  254.         $fix   $phpcsFile->addFixableError($error$stackPtr'MissingBrackets');
  255.  
  256.         if ($fix === false || $phpcsFile->fixer->enabled === false{
  257.             return;
  258.         }
  259.  
  260.         $tokens $phpcsFile->getTokens();
  261.  
  262.         $allowed = array(
  263.                     T_VARIABLE        => true,
  264.                     T_LNUMBER         => true,
  265.                     T_DNUMBER         => true,
  266.                     T_STRING          => true,
  267.                     T_WHITESPACE      => true,
  268.                     T_THIS            => true,
  269.                     T_SELF            => true,
  270.                     T_OBJECT_OPERATOR => true,
  271.                     T_DOUBLE_COLON    => true,
  272.                     T_MODULUS         => true,
  273.                     T_ISSET           => true,
  274.                     T_ARRAY           => true,
  275.                     T_NONE            => true,
  276.                    );
  277.  
  278.         // Find the first token in the expression.
  279.         for ($before ($stackPtr - 1)$before > 0; $before--{
  280.             // Special case for plus operators because we can't tell if they are used
  281.             // for addition or string contact. So assume string concat to be safe.
  282.             if ($phpcsFile->tokenizerType === 'JS' && $tokens[$before]['code'=== T_PLUS{
  283.                 break;
  284.             }
  285.  
  286.             if (isset(Tokens::$emptyTokens[$tokens[$before]['code']]=== true
  287.                 || isset(Tokens::$operators[$tokens[$before]['code']]=== true
  288.                 || isset(Tokens::$castTokens[$tokens[$before]['code']]=== true
  289.                 || isset($allowed[$tokens[$before]['code']]=== true
  290.             {
  291.                 continue;
  292.             }
  293.  
  294.             if ($tokens[$before]['code'=== T_CLOSE_PARENTHESIS{
  295.                 $before $tokens[$before]['parenthesis_opener'];
  296.                 continue;
  297.             }
  298.  
  299.             if ($tokens[$before]['code'=== T_CLOSE_SQUARE_BRACKET{
  300.                 $before $tokens[$before]['bracket_opener'];
  301.                 continue;
  302.             }
  303.  
  304.             break;
  305.         }//end for
  306.  
  307.         $before $phpcsFile->findNext(Tokens::$emptyTokens($before + 1)nulltrue);
  308.  
  309.         // Find the last token in the expression.
  310.         for ($after ($stackPtr + 1)$after $phpcsFile->numTokens; $after++{
  311.             // Special case for plus operators because we can't tell if they are used
  312.             // for addition or string contact. So assume string concat to be safe.
  313.             if ($phpcsFile->tokenizerType === 'JS' && $tokens[$after]['code'=== T_PLUS{
  314.                 break;
  315.             }
  316.  
  317.             if (isset(Tokens::$emptyTokens[$tokens[$after]['code']]=== true
  318.                 || isset(Tokens::$operators[$tokens[$after]['code']]=== true
  319.                 || isset(Tokens::$castTokens[$tokens[$after]['code']]=== true
  320.                 || isset($allowed[$tokens[$after]['code']]=== true
  321.             {
  322.                 continue;
  323.             }
  324.  
  325.             if ($tokens[$after]['code'=== T_OPEN_PARENTHESIS{
  326.                 $after $tokens[$after]['parenthesis_closer'];
  327.                 continue;
  328.             }
  329.  
  330.             if ($tokens[$after]['code'=== T_OPEN_SQUARE_BRACKET{
  331.                 $after $tokens[$after]['bracket_closer'];
  332.                 continue;
  333.             }
  334.  
  335.             break;
  336.         }//end for
  337.  
  338.         $after $phpcsFile->findPrevious(Tokens::$emptyTokens($after - 1)nulltrue);
  339.  
  340.         // Can only fix this error if both tokens are available for fixing.
  341.         // Adding one bracket without the other will create parse errors.
  342.         $phpcsFile->fixer->beginChangeset();
  343.         $phpcsFile->fixer->replaceToken($before'('.$tokens[$before]['content']);
  344.         $phpcsFile->fixer->replaceToken($after$tokens[$after]['content'].')');
  345.         $phpcsFile->fixer->endChangeset();
  346.  
  347.     }//end addMissingBracketsError()
  348.  
  349.  
  350. }//end class

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