*/ public function register(): array { return [ T_OPEN_TAG, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $openTagPointer */ public function process(File $phpcsFile, $openTagPointer): void { if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { return; } $tokens = $phpcsFile->getTokens(); $message = sprintf('Referencing general \%s; use \%s instead.', Exception::class, Throwable::class); $referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer); foreach ($referencedNames as $referencedName) { $resolvedName = NamespaceHelper::resolveClassName( $phpcsFile, $referencedName->getNameAsReferencedInFile(), $referencedName->getStartPointer(), ); if ($resolvedName !== '\\Exception') { continue; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencedName->getStartPointer() - 1); if (in_array($tokens[$previousPointer]['code'], [T_EXTENDS, T_NEW, T_INSTANCEOF], true)) { // Allow \Exception in extends and instantiating it continue; } if ($tokens[$previousPointer]['code'] === T_BITWISE_OR) { $previousPointer = TokenHelper::findPreviousExcluding( $phpcsFile, [...TokenHelper::INEFFECTIVE_TOKEN_CODES, ...TokenHelper::NAME_TOKEN_CODES, T_BITWISE_OR], $previousPointer - 1, ); } if ($tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS) { /** @var int $openParenthesisOpenerPointer */ $openParenthesisOpenerPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); if ($tokens[$openParenthesisOpenerPointer]['code'] === T_CATCH) { if ($this->searchForThrowableInNextCatches($phpcsFile, $openParenthesisOpenerPointer)) { continue; } } elseif ( array_key_exists('parenthesis_owner', $tokens[$previousPointer]) && $tokens[$tokens[$previousPointer]['parenthesis_owner']]['code'] === T_FUNCTION && $tokens[$previousPointer]['parenthesis_closer'] > $referencedName->getStartPointer() && SuppressHelper::isSniffSuppressed( $phpcsFile, $openParenthesisOpenerPointer, sprintf('%s.%s', self::NAME, self::CODE_REFERENCED_GENERAL_EXCEPTION), ) ) { continue; } } $fix = $phpcsFile->addFixableError( $message, $referencedName->getStartPointer(), self::CODE_REFERENCED_GENERAL_EXCEPTION, ); if (!$fix) { continue; } $phpcsFile->fixer->beginChangeset(); FixerHelper::change($phpcsFile, $referencedName->getStartPointer(), $referencedName->getEndPointer(), '\Throwable'); $phpcsFile->fixer->endChangeset(); } } private function searchForThrowableInNextCatches(File $phpcsFile, int $catchPointer): bool { $tokens = $phpcsFile->getTokens(); $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$catchPointer]['scope_closer'] + 1); while ($nextCatchPointer !== null) { $nextCatchToken = $tokens[$nextCatchPointer]; if ($nextCatchToken['code'] !== T_CATCH) { break; } $caughtTypes = CatchHelper::findCaughtTypesInCatch($phpcsFile, $nextCatchToken); if (in_array('\\Throwable', $caughtTypes, true)) { return true; } $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $nextCatchToken['scope_closer'] + 1); } return false; } }