Example #1
0
 /**
  * Processes this sniff, when one of its tokens is encountered.
  *
  * @param PHP_CodeSniffer_File $phpcsFile The current file being checked.
  * @param int                  $stackPtr  The position of the current token in
  *                                        the stack passed in $tokens.
  *
  * @return void
  */
 public function process(File $phpcsFile, $stackPtr)
 {
     $tokens = $phpcsFile->getTokens();
     // Skip default values in function declarations.
     if ($tokens[$stackPtr]['code'] === T_EQUAL || $tokens[$stackPtr]['code'] === T_MINUS) {
         if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
             $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
             $bracket = array_pop($parenthesis);
             if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
                 $function = $tokens[$bracket]['parenthesis_owner'];
                 if ($tokens[$function]['code'] === T_FUNCTION || $tokens[$function]['code'] === T_CLOSURE) {
                     return;
                 }
             }
         }
     }
     if ($tokens[$stackPtr]['code'] === T_EQUAL) {
         // Skip for '=&' case.
         if (isset($tokens[$stackPtr + 1]) === true && $tokens[$stackPtr + 1]['code'] === T_BITWISE_AND) {
             return;
         }
     }
     // Skip short ternary such as: "$foo = $bar ?: true;".
     if ($tokens[$stackPtr]['code'] === T_INLINE_THEN && $tokens[$stackPtr + 1]['code'] === T_INLINE_ELSE || $tokens[$stackPtr - 1]['code'] === T_INLINE_THEN && $tokens[$stackPtr]['code'] === T_INLINE_ELSE) {
         return;
     }
     if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
         // If it's not a reference, then we expect one space either side of the
         // bitwise operator.
         if ($phpcsFile->isReference($stackPtr) === true) {
             return;
         }
         // Check there is one space before the & operator.
         if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) {
             $error = 'Expected 1 space before "&" operator; 0 found';
             $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp');
             if ($fix === true) {
                 $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
             }
             $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
         } else {
             if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) {
                 $found = 'newline';
             } else {
                 $found = $tokens[$stackPtr - 1]['length'];
             }
             $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
             if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) {
                 $error = 'Expected 1 space before "&" operator; %s found';
                 $data = array($found);
                 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAmp', $data);
                 if ($fix === true) {
                     $phpcsFile->fixer->replaceToken($stackPtr - 1, ' ');
                 }
             }
         }
         //end if
         // Check there is one space after the & operator.
         if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) {
             $error = 'Expected 1 space after "&" operator; 0 found';
             $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp');
             if ($fix === true) {
                 $phpcsFile->fixer->addContent($stackPtr, ' ');
             }
             $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
         } else {
             if ($tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) {
                 $found = 'newline';
             } else {
                 $found = $tokens[$stackPtr + 1]['length'];
             }
             $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
             if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) {
                 $error = 'Expected 1 space after "&" operator; %s found';
                 $data = array($found);
                 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAmp', $data);
                 if ($fix === true) {
                     $phpcsFile->fixer->replaceToken($stackPtr + 1, ' ');
                 }
             }
         }
         //end if
         return;
     }
     //end if
     if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) {
         // Check minus spacing, but make sure we aren't just assigning
         // a minus value or returning one.
         $prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
         if ($tokens[$prev]['code'] === T_RETURN) {
             // Just returning a negative value; eg. (return -1).
             return;
         }
         if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) {
             // Just trying to operate on a negative value; eg. ($var * -1).
             return;
         }
         if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
             // Just trying to compare a negative value; eg. ($var === -1).
             return;
         }
         if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
             // Just trying to compare a negative value; eg. ($var || -1 === $b).
             return;
         }
         if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
             // Just trying to assign a negative value; eg. ($var = -1).
             return;
         }
         // A list of tokens that indicate that the token is not
         // part of an arithmetic operation.
         $invalidTokens = array(T_COMMA => true, T_OPEN_PARENTHESIS => true, T_OPEN_SQUARE_BRACKET => true, T_OPEN_SHORT_ARRAY => true, T_DOUBLE_ARROW => true, T_COLON => true, T_INLINE_THEN => true, T_INLINE_ELSE => true, T_CASE => true);
         if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
             // Just trying to use a negative value; eg. myFunction($var, -2).
             return;
         }
     }
     //end if
     $operator = $tokens[$stackPtr]['content'];
     if ($tokens[$stackPtr - 1]['code'] !== T_WHITESPACE) {
         $error = "Expected 1 space before \"{$operator}\"; 0 found";
         $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore');
         if ($fix === true) {
             $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
         }
         $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
     } else {
         if (isset(Tokens::$assignmentTokens[$tokens[$stackPtr]['code']]) === false) {
             // Don't throw an error for assignments, because other standards allow
             // multiple spaces there to align multiple assignments.
             if ($tokens[$stackPtr - 2]['line'] !== $tokens[$stackPtr]['line']) {
                 $found = 'newline';
             } else {
                 $found = $tokens[$stackPtr - 1]['length'];
             }
             $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
             if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) {
                 $error = 'Expected 1 space before "%s"; %s found';
                 $data = array($operator, $found);
                 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
                 if ($fix === true) {
                     $phpcsFile->fixer->beginChangeset();
                     if ($found === 'newline') {
                         $i = $stackPtr - 2;
                         while ($tokens[$i]['code'] === T_WHITESPACE) {
                             $phpcsFile->fixer->replaceToken($i, '');
                             $i--;
                         }
                     }
                     $phpcsFile->fixer->replaceToken($stackPtr - 1, ' ');
                     $phpcsFile->fixer->endChangeset();
                 }
             }
             //end if
         }
     }
     //end if
     if (isset($tokens[$stackPtr + 1]) === false) {
         return;
     }
     if ($tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) {
         $error = "Expected 1 space after \"{$operator}\"; 0 found";
         $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter');
         if ($fix === true) {
             $phpcsFile->fixer->addContent($stackPtr, ' ');
         }
         $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
     } else {
         if (isset($tokens[$stackPtr + 2]) === true && $tokens[$stackPtr + 2]['line'] !== $tokens[$stackPtr]['line']) {
             $found = 'newline';
         } else {
             $found = $tokens[$stackPtr + 1]['length'];
         }
         $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
         if ($found !== 1 && ($found !== 'newline' || $this->ignoreNewlines === false)) {
             $error = 'Expected 1 space after "%s"; %s found';
             $data = array($operator, $found);
             $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter', $data);
             if ($fix === true) {
                 $phpcsFile->fixer->replaceToken($stackPtr + 1, ' ');
             }
         }
     }
     //end if
 }
Example #2
0
 /**
  * Processes this test, when one of its tokens is encountered.
  *
  * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  * @param int                  $stackPtr  The position of the current token in the
  *                                        stack passed in $tokens.
  *
  * @return void
  */
 public function process(File $phpcsFile, $stackPtr)
 {
     $tokens = $phpcsFile->getTokens();
     if ($phpcsFile->tokenizerType === 'JS' && $tokens[$stackPtr]['code'] === T_PLUS) {
         // JavaScript uses the plus operator for string concatenation as well
         // so we cannot accurately determine if it is a string concat or addition.
         // So just ignore it.
         return;
     }
     // If the & is a reference, then we don't want to check for brackets.
     if ($tokens[$stackPtr]['code'] === T_BITWISE_AND && $phpcsFile->isReference($stackPtr) === true) {
         return;
     }
     // There is one instance where brackets aren't needed, which involves
     // the minus sign being used to assign a negative number to a variable.
     if ($tokens[$stackPtr]['code'] === T_MINUS) {
         // Check to see if we are trying to return -n.
         $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true);
         if ($tokens[$prev]['code'] === T_RETURN) {
             return;
         }
         $number = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
         if ($tokens[$number]['code'] === T_LNUMBER || $tokens[$number]['code'] === T_DNUMBER) {
             $previous = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
             if ($previous !== false) {
                 $isAssignment = in_array($tokens[$previous]['code'], Tokens::$assignmentTokens);
                 $isEquality = in_array($tokens[$previous]['code'], Tokens::$equalityTokens);
                 $isComparison = in_array($tokens[$previous]['code'], Tokens::$comparisonTokens);
                 if ($isAssignment === true || $isEquality === true || $isComparison === true) {
                     // This is a negative assignment or comparison.
                     // We need to check that the minus and the number are
                     // adjacent.
                     if ($number - $stackPtr !== 1) {
                         $error = 'No space allowed between minus sign and number';
                         $phpcsFile->addError($error, $stackPtr, 'SpacingAfterMinus');
                     }
                     return;
                 }
             }
         }
     }
     //end if
     // Tokens that are allowed inside a bracketed operation.
     $allowed = array(T_VARIABLE, T_LNUMBER, T_DNUMBER, T_STRING, T_WHITESPACE, T_THIS, T_SELF, T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET, T_MODULUS, T_NONE);
     $allowed += Tokens::$operators;
     $lastBracket = false;
     if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
         $parenthesis = array_reverse($tokens[$stackPtr]['nested_parenthesis'], true);
         foreach ($parenthesis as $bracket => $endBracket) {
             $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, $bracket - 1, null, true);
             $prevCode = $tokens[$prevToken]['code'];
             if ($prevCode === T_ISSET) {
                 // This operation is inside an isset() call, but has
                 // no bracket of it's own.
                 break;
             }
             if ($prevCode === T_STRING || $prevCode === T_SWITCH) {
                 // We allow simple operations to not be bracketed.
                 // For example, ceil($one / $two).
                 for ($prev = $stackPtr - 1; $prev > $bracket; $prev--) {
                     if (in_array($tokens[$prev]['code'], $allowed) === true) {
                         continue;
                     }
                     if ($tokens[$prev]['code'] === T_CLOSE_PARENTHESIS) {
                         $prev = $tokens[$prev]['parenthesis_opener'];
                     } else {
                         break;
                     }
                 }
                 if ($prev !== $bracket) {
                     break;
                 }
                 for ($next = $stackPtr + 1; $next < $endBracket; $next++) {
                     if (in_array($tokens[$next]['code'], $allowed) === true) {
                         continue;
                     }
                     if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
                         $next = $tokens[$next]['parenthesis_closer'];
                     } else {
                         break;
                     }
                 }
                 if ($next !== $endBracket) {
                     break;
                 }
             }
             //end if
             if (in_array($prevCode, Tokens::$scopeOpeners) === true) {
                 // This operation is inside a control structure like FOREACH
                 // or IF, but has no bracket of it's own.
                 // The only control structure allowed to do this is SWITCH.
                 if ($prevCode !== T_SWITCH) {
                     break;
                 }
             }
             if ($prevCode === T_OPEN_PARENTHESIS) {
                 // These are two open parenthesis in a row. If the current
                 // one doesn't enclose the operator, go to the previous one.
                 if ($endBracket < $stackPtr) {
                     continue;
                 }
             }
             $lastBracket = $bracket;
             break;
         }
         //end foreach
     }
     //end if
     if ($lastBracket === false) {
         // It is not in a bracketed statement at all.
         $previousToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
         if ($previousToken !== false) {
             // A list of tokens that indicate that the token is not
             // part of an arithmetic operation.
             $invalidTokens = array(T_COMMA, T_COLON, T_OPEN_PARENTHESIS, T_OPEN_SQUARE_BRACKET, T_OPEN_SHORT_ARRAY, T_CASE);
             if (in_array($tokens[$previousToken]['code'], $invalidTokens) === false) {
                 $this->addMissingBracketsError($phpcsFile, $stackPtr);
             }
             return;
         }
     } else {
         if ($tokens[$lastBracket]['parenthesis_closer'] < $stackPtr) {
             // There are a set of brackets in front of it that don't include it.
             $this->addMissingBracketsError($phpcsFile, $stackPtr);
             return;
         } else {
             // We are enclosed in a set of bracket, so the last thing to
             // check is that we are not also enclosed in square brackets
             // like this: ($array[$index + 1]), which is invalid.
             $brackets = array(T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET);
             $squareBracket = $phpcsFile->findPrevious($brackets, $stackPtr - 1, $lastBracket);
             if ($squareBracket !== false && $tokens[$squareBracket]['code'] === T_OPEN_SQUARE_BRACKET) {
                 $closeSquareBracket = $phpcsFile->findNext($brackets, $stackPtr + 1);
                 if ($closeSquareBracket !== false && $tokens[$closeSquareBracket]['code'] === T_CLOSE_SQUARE_BRACKET) {
                     $this->addMissingBracketsError($phpcsFile, $stackPtr);
                 }
             }
             return;
         }
     }
     //end if
     $lastAssignment = $phpcsFile->findPrevious(Tokens::$assignmentTokens, $stackPtr, null, false, null, true);
     if ($lastAssignment !== false && $lastAssignment > $lastBracket) {
         $this->addMissingBracketsError($phpcsFile, $stackPtr);
     }
 }