*/ public function register(): array { return [ T_ISSET, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_ISSET) { $this->checkIsset($phpcsFile, $pointer); } else { $this->checkIdenticalOperator($phpcsFile, $pointer); } } public function checkIsset(File $phpcsFile, int $issetPointer): void { $tokens = $phpcsFile->getTokens(); $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $issetPointer - 1); if ($tokens[$previousPointer]['code'] === T_BOOLEAN_NOT) { return; } if (in_array($tokens[$previousPointer]['code'], Tokens::$booleanOperators, true)) { return; } $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $issetPointer + 1); $closeParenthesisPointer = $tokens[$openParenthesisPointer]['parenthesis_closer']; /** @var int $inlineThenPointer */ $inlineThenPointer = TokenHelper::findNextEffective($phpcsFile, $closeParenthesisPointer + 1); if ($tokens[$inlineThenPointer]['code'] !== T_INLINE_THEN) { return; } $commaPointer = TokenHelper::findNext($phpcsFile, T_COMMA, $openParenthesisPointer + 1, $closeParenthesisPointer); if ($commaPointer !== null) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $variableContent = IdentificatorHelper::getContent($phpcsFile, $openParenthesisPointer + 1, $closeParenthesisPointer - 1); $thenContent = IdentificatorHelper::getContent($phpcsFile, $inlineThenPointer + 1, $inlineElsePointer - 1); if ($variableContent !== $thenContent) { return; } $fix = $phpcsFile->addFixableError( 'Use null coalesce operator instead of ternary operator.', $inlineThenPointer, self::CODE_NULL_COALESCE_OPERATOR_NOT_USED ); if (!$fix) { return; } $startPointer = $issetPointer; if (in_array($tokens[$previousPointer]['code'], Tokens::$castTokens, true)) { $startPointer = $previousPointer; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $startPointer, $inlineElsePointer, sprintf('%s ??', $variableContent)); $phpcsFile->fixer->endChangeset(); } public function checkIdenticalOperator(File $phpcsFile, int $identicalOperator): void { $tokens = $phpcsFile->getTokens(); /** @var int $pointerBeforeIdenticalOperator */ $pointerBeforeIdenticalOperator = TokenHelper::findPreviousEffective($phpcsFile, $identicalOperator - 1); /** @var int $pointerAfterIdenticalOperator */ $pointerAfterIdenticalOperator = TokenHelper::findNextEffective($phpcsFile, $identicalOperator + 1); if ( $tokens[$pointerBeforeIdenticalOperator]['code'] !== T_NULL && $tokens[$pointerAfterIdenticalOperator]['code'] !== T_NULL ) { return; } $isYodaCondition = $tokens[$pointerBeforeIdenticalOperator]['code'] === T_NULL; $variableEndPointer = $isYodaCondition ? $pointerAfterIdenticalOperator : $pointerBeforeIdenticalOperator; $tmpPointer = $variableEndPointer; while ($tokens[$tmpPointer]['code'] === T_CLOSE_PARENTHESIS) { /** @var int $tmpPointer */ $tmpPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$tmpPointer]['parenthesis_opener'] - 1); } $variableStartPointer = IdentificatorHelper::findStartPointer($phpcsFile, $tmpPointer); if ($variableStartPointer === null) { return; } $pointerBeforeCondition = TokenHelper::findPreviousEffective( $phpcsFile, ($isYodaCondition ? $pointerBeforeIdenticalOperator : $variableStartPointer) - 1 ); if (in_array($tokens[$pointerBeforeCondition]['code'], Tokens::$booleanOperators, true)) { return; } /** @var int $inlineThenPointer */ $inlineThenPointer = TokenHelper::findNextEffective( $phpcsFile, ($isYodaCondition ? $variableEndPointer : $pointerAfterIdenticalOperator) + 1 ); if ($tokens[$inlineThenPointer]['code'] !== T_INLINE_THEN) { return; } $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); $pointerAfterInlineElseEnd = TokenHelper::findNextEffective($phpcsFile, $inlineElseEndPointer + 1); $variableContent = IdentificatorHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer); /** @var int $compareToStartPointer */ $compareToStartPointer = TokenHelper::findNextEffective( $phpcsFile, ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL ? $inlineElsePointer : $inlineThenPointer) + 1 ); /** @var int $compareToEndPointer */ $compareToEndPointer = TokenHelper::findPreviousEffective( $phpcsFile, ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL ? $pointerAfterInlineElseEnd : $inlineElsePointer) - 1 ); $compareToContent = IdentificatorHelper::getContent($phpcsFile, $compareToStartPointer, $compareToEndPointer); if ($compareToContent !== $variableContent) { return; } $fix = $phpcsFile->addFixableError( 'Use null coalesce operator instead of ternary operator.', $inlineThenPointer, self::CODE_NULL_COALESCE_OPERATOR_NOT_USED ); if (!$fix) { return; } /** @var int $conditionStart */ $conditionStart = $isYodaCondition ? $pointerBeforeIdenticalOperator : $variableStartPointer; $variableContent = trim(TokenHelper::getContent($phpcsFile, $variableStartPointer, $variableEndPointer)); $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->replaceToken($conditionStart, sprintf('%s ??', $variableContent)); if ($tokens[$identicalOperator]['code'] === T_IS_IDENTICAL) { FixerHelper::removeBetweenIncluding($phpcsFile, $conditionStart + 1, $inlineThenPointer); $pointerBeforeInlineElse = TokenHelper::findPreviousEffective($phpcsFile, $inlineElsePointer - 1); FixerHelper::removeBetweenIncluding($phpcsFile, $pointerBeforeInlineElse + 1, $inlineElseEndPointer); } else { FixerHelper::removeBetweenIncluding($phpcsFile, $conditionStart + 1, $inlineElsePointer); } $phpcsFile->fixer->endChangeset(); } }