Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. UselessParenthesesSniff.php

class UselessParenthesesSniff

Hierarchy

  • class \SlevomatCodingStandard\Sniffs\PHP\UselessParenthesesSniff implements \PHP_CodeSniffer\Sniffs\Sniff

Expanded class hierarchy of UselessParenthesesSniff

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/PHP/UselessParenthesesSniff.php, line 75

Namespace

SlevomatCodingStandard\Sniffs\PHP
View source
class UselessParenthesesSniff implements Sniff {
    public const CODE_USELESS_PARENTHESES = 'UselessParentheses';
    private const OPERATORS = [
        T_POW,
        T_MULTIPLY,
        T_DIVIDE,
        T_MODULUS,
        T_PLUS,
        T_MINUS,
        T_STRING_CONCAT,
    ];
    private const OPERATOR_GROUPS = [
        T_POW => 1,
        T_MULTIPLY => 2,
        T_DIVIDE => 2,
        T_MODULUS => 3,
        T_PLUS => 4,
        T_MINUS => 4,
        T_STRING_CONCAT => 5,
    ];
    
    /** @var bool */
    public $ignoreComplexTernaryConditions = false;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        return [
            T_OPEN_PARENTHESIS,
        ];
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $parenthesisOpenerPointer
     */
    public function process(File $phpcsFile, $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        if (array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) {
            return;
        }
        if (!array_key_exists('parenthesis_closer', $tokens[$parenthesisOpenerPointer])) {
            return;
        }
        
        /** @var int $pointerBeforeParenthesisOpener */
        $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], array_merge(TokenHelper::getNameTokenCodes(), [
            T_VARIABLE,
            T_ISSET,
            T_UNSET,
            T_EMPTY,
            T_CLOSURE,
            T_FN,
            T_USE,
            T_ANON_CLASS,
            T_NEW,
            T_SELF,
            T_STATIC,
            T_PARENT,
            T_EXIT,
            T_CLOSE_PARENTHESIS,
            T_EVAL,
            T_LIST,
            T_INCLUDE,
            T_INCLUDE_ONCE,
            T_REQUIRE,
            T_REQUIRE_ONCE,
            T_INT_CAST,
            T_DOUBLE_CAST,
            T_STRING_CAST,
            T_ARRAY_CAST,
            T_OBJECT_CAST,
            T_BOOL_CAST,
            T_UNSET_CAST,
            T_MATCH,
        ]), true)) {
            return;
        }
        
        /** @var int $pointerAfterParenthesisOpener */
        $pointerAfterParenthesisOpener = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        if (in_array($tokens[$pointerAfterParenthesisOpener]['code'], [
            T_NEW,
            T_CLONE,
            T_YIELD,
            T_YIELD_FROM,
            T_REQUIRE,
            T_REQUIRE_ONCE,
            T_INCLUDE,
            T_INCLUDE_ONCE,
            T_ARRAY_CAST,
        ], true)) {
            return;
        }
        if (TokenHelper::findNext($phpcsFile, T_EQUAL, $parenthesisOpenerPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) !== null) {
            return;
        }
        $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1);
        if ($pointerAfterParenthesisCloser !== null && $tokens[$pointerAfterParenthesisCloser]['code'] === T_OPEN_PARENTHESIS) {
            return;
        }
        if (IdentificatorHelper::findStartPointer($phpcsFile, $pointerBeforeParenthesisOpener) !== null) {
            return;
        }
        $this->checkParenthesesAroundConditionInTernaryOperator($phpcsFile, $parenthesisOpenerPointer);
        $this->checkParenthesesAroundCaseInSwitch($phpcsFile, $parenthesisOpenerPointer);
        $this->checkParenthesesAroundVariableOrFunctionCall($phpcsFile, $parenthesisOpenerPointer);
        $this->checkParenthesesAroundString($phpcsFile, $parenthesisOpenerPointer);
        $this->checkParenthesesAroundOperators($phpcsFile, $parenthesisOpenerPointer);
    }
    private function checkParenthesesAroundConditionInTernaryOperator(File $phpcsFile, int $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer'];
        $ternaryOperatorPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1);
        if ($tokens[$ternaryOperatorPointer]['code'] !== T_INLINE_THEN) {
            return;
        }
        if (TokenHelper::findNext($phpcsFile, [
            T_LOGICAL_AND,
            T_LOGICAL_OR,
            T_LOGICAL_XOR,
        ], $parenthesisOpenerPointer + 1, $parenthesisCloserPointer) !== null) {
            return;
        }
        $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_BOOLEAN_NOT) {
            return;
        }
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$comparisonTokens, true)) {
            return;
        }
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true)) {
            return;
        }
        if ($this->ignoreComplexTernaryConditions) {
            if (TokenHelper::findNext($phpcsFile, Tokens::$booleanOperators, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer) !== null) {
                return;
            }
            if (TokenHelper::findNextContent($phpcsFile, T_WHITESPACE, $phpcsFile->eolChar, $parenthesisOpenerPointer + 1, $parenthesisCloserPointer) !== null) {
                return;
            }
        }
        $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1);
        for ($i = $contentStartPointer; $i <= $contentEndPointer; $i++) {
            if ($tokens[$i]['code'] === T_INLINE_THEN) {
                return;
            }
        }
        $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1);
        FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $parenthesisCloserPointer);
        $phpcsFile->fixer
            ->endChangeset();
    }
    private function checkParenthesesAroundCaseInSwitch(File $phpcsFile, int $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        if ($tokens[$pointerBeforeParenthesisOpener]['code'] !== T_CASE) {
            return;
        }
        $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1);
        if ($tokens[$pointerAfterParenthesisCloser]['code'] !== T_COLON) {
            return;
        }
        $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES);
        if (!$fix) {
            return;
        }
        $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] - 1);
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1);
        FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
        $phpcsFile->fixer
            ->endChangeset();
    }
    private function checkParenthesesAroundVariableOrFunctionCall(File $phpcsFile, int $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        $operatorsPointers = TokenHelper::findNextAll($phpcsFile, self::OPERATORS, $parenthesisOpenerPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
        if ($operatorsPointers !== []) {
            return;
        }
        $casePointer = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        if ($tokens[$casePointer]['code'] === T_CASE) {
            return;
        }
        $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true)) {
            return;
        }
        $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1);
        if (in_array($tokens[$pointerAfterParenthesisCloser]['code'], [
            T_INLINE_THEN,
            T_OPEN_PARENTHESIS,
            T_SR,
        ], true)) {
            return;
        }
        
        /** @var int $contentStartPointer */
        $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        if ($tokens[$contentStartPointer]['code'] === T_CONSTANT_ENCAPSED_STRING) {
            return;
        }
        $notBooleanNotOperatorPointer = $contentStartPointer;
        if ($tokens[$contentStartPointer]['code'] === T_BOOLEAN_NOT) {
            
            /** @var int $notBooleanNotOperatorPointer */
            $notBooleanNotOperatorPointer = TokenHelper::findNextEffective($phpcsFile, $contentStartPointer + 1);
        }
        if (in_array($tokens[$notBooleanNotOperatorPointer]['code'], array_merge([
            T_SELF,
            T_STATIC,
            T_PARENT,
            T_VARIABLE,
            T_DOLLAR,
        ], TokenHelper::getNameTokenCodes()), true)) {
            $contentEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $notBooleanNotOperatorPointer);
            if ($contentEndPointer === null && in_array($tokens[$notBooleanNotOperatorPointer]['code'], TokenHelper::getNameTokenCodes(), true)) {
                $nextPointer = TokenHelper::findNextEffective($phpcsFile, $contentStartPointer + 1);
                if ($tokens[$nextPointer]['code'] === T_OPEN_PARENTHESIS) {
                    $contentEndPointer = $contentStartPointer;
                }
            }
            do {
                $nextPointer = TokenHelper::findNextEffective($phpcsFile, $contentEndPointer + 1);
                if ($tokens[$nextPointer]['code'] !== T_OPEN_PARENTHESIS) {
                    break;
                }
                $contentEndPointer = $tokens[$nextPointer]['parenthesis_closer'];
            } while (true);
        }
        else {
            $nextPointer = TokenHelper::findNext($phpcsFile, T_OPEN_PARENTHESIS, $notBooleanNotOperatorPointer + 1);
            if ($nextPointer === null || !isset($tokens[$nextPointer]['parenthesis_closer'])) {
                return;
            }
            $contentEndPointer = $tokens[$nextPointer]['parenthesis_closer'];
        }
        $pointerAfterContent = TokenHelper::findNextEffective($phpcsFile, $contentEndPointer + 1);
        if ($pointerAfterContent !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) {
            return;
        }
        $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1);
        FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
        $phpcsFile->fixer
            ->endChangeset();
    }
    private function checkParenthesesAroundString(File $phpcsFile, int $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        
        /** @var int $stringPointer */
        $stringPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        if ($tokens[$stringPointer]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
            return;
        }
        $pointerAfterString = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1);
        if ($pointerAfterString !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) {
            return;
        }
        $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $stringPointer - 1);
        FixerHelper::removeBetweenIncluding($phpcsFile, $stringPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
        $phpcsFile->fixer
            ->endChangeset();
    }
    private function checkParenthesesAroundOperators(File $phpcsFile, int $parenthesisOpenerPointer) : void {
        $tokens = $phpcsFile->getTokens();
        $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
        $pointerAfterParenthesisCloser = TokenHelper::findNextEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1);
        if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_MINUS) {
            $pointerBeforeMinus = TokenHelper::findPreviousEffective($phpcsFile, $pointerBeforeParenthesisOpener - 1);
            if (!in_array($tokens[$pointerBeforeMinus]['code'], [
                T_DNUMBER,
                T_LNUMBER,
            ], true)) {
                return;
            }
        }
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], Tokens::$booleanOperators, true) || in_array($tokens[$pointerAfterParenthesisCloser]['code'], Tokens::$booleanOperators, true)) {
            return;
        }
        $operatorsPointers = [];
        $actualStartPointer = $parenthesisOpenerPointer + 1;
        while (true) {
            $pointer = TokenHelper::findNext($phpcsFile, array_merge(self::OPERATORS, [
                T_OPEN_PARENTHESIS,
                T_INLINE_THEN,
                T_COALESCE,
                T_BITWISE_AND,
                T_BITWISE_OR,
                T_BITWISE_XOR,
            ], Tokens::$comparisonTokens), $actualStartPointer, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
            if ($pointer === null) {
                break;
            }
            if (in_array($tokens[$pointer]['code'], [
                T_INLINE_THEN,
                T_COALESCE,
                T_BITWISE_AND,
                T_BITWISE_OR,
                T_BITWISE_XOR,
            ], true)) {
                return;
            }
            if (in_array($tokens[$pointer]['code'], Tokens::$comparisonTokens, true)) {
                return;
            }
            if ($tokens[$pointer]['code'] === T_OPEN_PARENTHESIS) {
                $actualStartPointer = $tokens[$pointer]['parenthesis_closer'] + 1;
                continue;
            }
            $operatorsPointers[] = $pointer;
            $actualStartPointer = $pointer + 1;
        }
        if (count($operatorsPointers) === 0) {
            return;
        }
        if ($tokens[$pointerBeforeParenthesisOpener]['code'] !== T_EQUAL || $tokens[$pointerAfterParenthesisCloser]['code'] !== T_SEMICOLON) {
            $operatorsGroups = array_map(static function (int $operatorPointer) use ($tokens) : int {
                return self::OPERATOR_GROUPS[$tokens[$operatorPointer]['code']];
            }, $operatorsPointers);
            if (count($operatorsGroups) > 1) {
                return;
            }
        }
        $firstOperatorPointer = $operatorsPointers[0];
        if (in_array($tokens[$pointerBeforeParenthesisOpener]['code'], self::OPERATORS, true)) {
            if (self::OPERATOR_GROUPS[$tokens[$firstOperatorPointer]['code']] !== self::OPERATOR_GROUPS[$tokens[$pointerBeforeParenthesisOpener]['code']]) {
                return;
            }
            if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_MINUS && in_array($tokens[$firstOperatorPointer]['code'], [
                T_PLUS,
                T_MINUS,
            ], true)) {
                return;
            }
            if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_DIVIDE && in_array($tokens[$firstOperatorPointer]['code'], [
                T_DIVIDE,
                T_MULTIPLY,
            ], true)) {
                return;
            }
            if ($tokens[$pointerBeforeParenthesisOpener]['code'] === T_MODULUS && $tokens[$firstOperatorPointer]['code'] === T_MODULUS) {
                return;
            }
        }
        $lastOperatorPointer = $operatorsPointers[count($operatorsPointers) - 1];
        if (in_array($tokens[$pointerAfterParenthesisCloser]['code'], self::OPERATORS, true) && self::OPERATOR_GROUPS[$tokens[$lastOperatorPointer]['code']] !== self::OPERATOR_GROUPS[$tokens[$pointerAfterParenthesisCloser]['code']]) {
            return;
        }
        $fix = $phpcsFile->addFixableError('Useless parentheses.', $parenthesisOpenerPointer, self::CODE_USELESS_PARENTHESES);
        if (!$fix) {
            return;
        }
        $contentStartPointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisOpenerPointer + 1);
        $contentEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] - 1);
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($phpcsFile, $parenthesisOpenerPointer, $contentStartPointer - 1);
        FixerHelper::removeBetweenIncluding($phpcsFile, $contentEndPointer + 1, $tokens[$parenthesisOpenerPointer]['parenthesis_closer']);
        $phpcsFile->fixer
            ->endChangeset();
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
UselessParenthesesSniff::$ignoreComplexTernaryConditions public property @var bool
UselessParenthesesSniff::checkParenthesesAroundCaseInSwitch private function
UselessParenthesesSniff::checkParenthesesAroundConditionInTernaryOperator private function
UselessParenthesesSniff::checkParenthesesAroundOperators private function
UselessParenthesesSniff::checkParenthesesAroundString private function
UselessParenthesesSniff::checkParenthesesAroundVariableOrFunctionCall private function
UselessParenthesesSniff::CODE_USELESS_PARENTHESES public constant
UselessParenthesesSniff::OPERATORS private constant
UselessParenthesesSniff::OPERATOR_GROUPS private constant
UselessParenthesesSniff::process public function * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*
Overrides Sniff::process
UselessParenthesesSniff::register public function * Overrides Sniff::register

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal