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.         $lastBracket = false;
  99.         if (isset($tokens[$stackPtr]['nested_parenthesis']=== true{
  100.             $parenthesis array_reverse($tokens[$stackPtr]['nested_parenthesis']true);
  101.             foreach ($parenthesis as $bracket => $endBracket{
  102.                 $prevToken $phpcsFile->findPrevious(T_WHITESPACE($bracket - 1)nulltrue);
  103.                 $prevCode  $tokens[$prevToken]['code'];
  104.  
  105.                 if ($prevCode === T_ISSET{
  106.                     // This operation is inside an isset() call, but has
  107.                     // no bracket of it's own.
  108.                     break;
  109.                 }
  110.  
  111.                 if ($prevCode === T_STRING || $prevCode === T_SWITCH{
  112.                     // We allow very simple operations to not be bracketed.
  113.                     // For example, ceil($one / $two).
  114.                     $allowed = array(
  115.                                 T_VARIABLE,
  116.                                 T_LNUMBER,
  117.                                 T_DNUMBER,
  118.                                 T_STRING,
  119.                                 T_WHITESPACE,
  120.                                 T_THIS,
  121.                                 T_SELF,
  122.                                 T_OBJECT_OPERATOR,
  123.                                 T_DOUBLE_COLON,
  124.                                 T_OPEN_SQUARE_BRACKET,
  125.                                 T_CLOSE_SQUARE_BRACKET,
  126.                                 T_MODULUS,
  127.                                );
  128.  
  129.                     for ($prev ($stackPtr - 1)$prev $bracket$prev--{
  130.                         if (in_array($tokens[$prev]['code']$allowed=== true{
  131.                             continue;
  132.                         }
  133.  
  134.                         if ($tokens[$prev]['code'=== T_CLOSE_PARENTHESIS{
  135.                             $prev $tokens[$prev]['parenthesis_opener'];
  136.                         else {
  137.                             break;
  138.                         }
  139.                     }
  140.  
  141.                     if ($prev !== $bracket{
  142.                         break;
  143.                     }
  144.  
  145.                     for ($next ($stackPtr + 1)$next $endBracket$next++{
  146.                         if (in_array($tokens[$next]['code']$allowed=== true{
  147.                             continue;
  148.                         }
  149.  
  150.                         if ($tokens[$next]['code'=== T_OPEN_PARENTHESIS{
  151.                             $next $tokens[$next]['parenthesis_closer'];
  152.                         else {
  153.                             break;
  154.                         }
  155.                     }
  156.  
  157.                     if ($next !== $endBracket{
  158.                         break;
  159.                     }
  160.                 }//end if
  161.  
  162.                 if (in_array($prevCodeTokens::$scopeOpeners=== true{
  163.                     // This operation is inside a control structure like FOREACH
  164.                     // or IF, but has no bracket of it's own.
  165.                     // The only control structure allowed to do this is SWITCH.
  166.                     if ($prevCode !== T_SWITCH{
  167.                         break;
  168.                     }
  169.                 }
  170.  
  171.                 if ($prevCode === T_OPEN_PARENTHESIS{
  172.                     // These are two open parenthesis in a row. If the current
  173.                     // one doesn't enclose the operator, go to the previous one.
  174.                     if ($endBracket $stackPtr{
  175.                         continue;
  176.                     }
  177.                 }
  178.  
  179.                 $lastBracket $bracket;
  180.                 break;
  181.             }//end foreach
  182.         }//end if
  183.  
  184.         if ($lastBracket === false{
  185.             // It is not in a bracketed statement at all.
  186.             $previousToken $phpcsFile->findPrevious(T_WHITESPACE($stackPtr - 1)nulltruenulltrue);
  187.             if ($previousToken !== false{
  188.                 // A list of tokens that indicate that the token is not
  189.                 // part of an arithmetic operation.
  190.                 $invalidTokens = array(
  191.                                   T_COMMA,
  192.                                   T_COLON,
  193.                                   T_OPEN_PARENTHESIS,
  194.                                   T_OPEN_SQUARE_BRACKET,
  195.                                   T_OPEN_SHORT_ARRAY,
  196.                                   T_CASE,
  197.                                  );
  198.  
  199.                 if (in_array($tokens[$previousToken]['code']$invalidTokens=== false{
  200.                     $this->addMissingBracketsError($phpcsFile$stackPtr);
  201.                 }
  202.  
  203.                 return;
  204.             }
  205.         else if ($tokens[$lastBracket]['parenthesis_closer'$stackPtr{
  206.             // There are a set of brackets in front of it that don't include it.
  207.             $this->addMissingBracketsError($phpcsFile$stackPtr);
  208.             return;
  209.         else {
  210.             // We are enclosed in a set of bracket, so the last thing to
  211.             // check is that we are not also enclosed in square brackets
  212.             // like this: ($array[$index + 1]), which is invalid.
  213.             $brackets = array(
  214.                          T_OPEN_SQUARE_BRACKET,
  215.                          T_CLOSE_SQUARE_BRACKET,
  216.                         );
  217.  
  218.             $squareBracket $phpcsFile->findPrevious($brackets($stackPtr - 1)$lastBracket);
  219.             if ($squareBracket !== false && $tokens[$squareBracket]['code'=== T_OPEN_SQUARE_BRACKET{
  220.                 $closeSquareBracket $phpcsFile->findNext($brackets($stackPtr + 1));
  221.                 if ($closeSquareBracket !== false && $tokens[$closeSquareBracket]['code'=== T_CLOSE_SQUARE_BRACKET{
  222.                     $this->addMissingBracketsError($phpcsFile$stackPtr);
  223.                 }
  224.             }
  225.  
  226.             return;
  227.         }//end if
  228.  
  229.         $lastAssignment $phpcsFile->findPrevious(Tokens::$assignmentTokens$stackPtrnullfalsenulltrue);
  230.         if ($lastAssignment !== false && $lastAssignment $lastBracket{
  231.             $this->addMissingBracketsError($phpcsFile$stackPtr);
  232.         }
  233.  
  234.     }//end process()
  235.  
  236.  
  237.     /**
  238.      * Add and fix the missing brackets error.
  239.      *
  240.      * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  241.      * @param int                  $stackPtr  The position of the current token in the
  242.      *                                         stack passed in $tokens.
  243.      *
  244.      * @return void 
  245.      */
  246.     public function addMissingBracketsError($phpcsFile$stackPtr)
  247.     {
  248.         $error 'Arithmetic operation must be bracketed';
  249.         $fix   $phpcsFile->addFixableError($error$stackPtr'MissingBrackets');
  250.  
  251.         if ($fix === false || $phpcsFile->fixer->enabled === false{
  252.             return;
  253.         }
  254.  
  255.         $tokens $phpcsFile->getTokens();
  256.  
  257.         $allowed = array(
  258.                     T_VARIABLE        => true,
  259.                     T_LNUMBER         => true,
  260.                     T_DNUMBER         => true,
  261.                     T_STRING          => true,
  262.                     T_WHITESPACE      => true,
  263.                     T_THIS            => true,
  264.                     T_SELF            => true,
  265.                     T_OBJECT_OPERATOR => true,
  266.                     T_DOUBLE_COLON    => true,
  267.                     T_MODULUS         => true,
  268.                     T_ISSET           => true,
  269.                     T_ARRAY           => true,
  270.                    );
  271.  
  272.         // Find the first token in the expression.
  273.         for ($before ($stackPtr - 1)$before > 0; $before--{
  274.             // Special case for plus operators because we can't tell if they are used
  275.             // for addition or string contact. So assume string concat to be safe.
  276.             if ($phpcsFile->tokenizerType === 'JS' && $tokens[$before]['code'=== T_PLUS{
  277.                 break;
  278.             }
  279.  
  280.             if (isset(Tokens::$emptyTokens[$tokens[$before]['code']]=== true
  281.                 || isset(Tokens::$operators[$tokens[$before]['code']]=== true
  282.                 || isset(Tokens::$castTokens[$tokens[$before]['code']]=== true
  283.                 || isset($allowed[$tokens[$before]['code']]=== true
  284.             {
  285.                 continue;
  286.             }
  287.  
  288.             if ($tokens[$before]['code'=== T_CLOSE_PARENTHESIS{
  289.                 $before $tokens[$before]['parenthesis_opener'];
  290.                 continue;
  291.             }
  292.  
  293.             if ($tokens[$before]['code'=== T_CLOSE_SQUARE_BRACKET{
  294.                 $before $tokens[$before]['bracket_opener'];
  295.                 continue;
  296.             }
  297.  
  298.             break;
  299.         }//end for
  300.  
  301.         $before $phpcsFile->findNext(Tokens::$emptyTokens($before + 1)nulltrue);
  302.  
  303.         // Find the last token in the expression.
  304.         for ($after ($stackPtr + 1)$after $phpcsFile->numTokens; $after++{
  305.             // Special case for plus operators because we can't tell if they are used
  306.             // for addition or string contact. So assume string concat to be safe.
  307.             if ($phpcsFile->tokenizerType === 'JS' && $tokens[$after]['code'=== T_PLUS{
  308.                 break;
  309.             }
  310.  
  311.             if (isset(Tokens::$emptyTokens[$tokens[$after]['code']]=== true
  312.                 || isset(Tokens::$operators[$tokens[$after]['code']]=== true
  313.                 || isset(Tokens::$castTokens[$tokens[$after]['code']]=== true
  314.                 || isset($allowed[$tokens[$after]['code']]=== true
  315.             {
  316.                 continue;
  317.             }
  318.  
  319.             if ($tokens[$after]['code'=== T_OPEN_PARENTHESIS{
  320.                 $after $tokens[$after]['parenthesis_closer'];
  321.                 continue;
  322.             }
  323.  
  324.             if ($tokens[$after]['code'=== T_OPEN_SQUARE_BRACKET{
  325.                 $after $tokens[$after]['bracket_closer'];
  326.                 continue;
  327.             }
  328.  
  329.             break;
  330.         }//end for
  331.  
  332.         $after $phpcsFile->findPrevious(Tokens::$emptyTokens($after - 1)nulltrue);
  333.  
  334.         // Can only fix this error if both tokens are available for fixing.
  335.         // Adding one bracket without the other will create parse errors.
  336.         $phpcsFile->fixer->beginChangeset();
  337.         $phpcsFile->fixer->replaceToken($before'('.$tokens[$before]['content']);
  338.         $phpcsFile->fixer->replaceToken($after$tokens[$after]['content'].')');
  339.         $phpcsFile->fixer->endChangeset();
  340.  
  341.     }//end addMissingBracketsError()
  342.  
  343.  
  344. }//end class

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