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

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