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

Breadcrumb

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

class ClassMemberSpacingSniff

Hierarchy

  • class \SlevomatCodingStandard\Sniffs\Classes\ClassMemberSpacingSniff implements \PHP_CodeSniffer\Sniffs\Sniff

Expanded class hierarchy of ClassMemberSpacingSniff

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.php, line 40

Namespace

SlevomatCodingStandard\Sniffs\Classes
View source
class ClassMemberSpacingSniff implements Sniff {
    public const CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS = 'IncorrectCountOfBlankLinesBetweenMembers';
    
    /** @var int */
    public $linesCountBetweenMembers = 1;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        return TokenHelper::$typeWithAnonymousClassKeywordTokenCodes;
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $classPointer
     */
    public function process(File $phpcsFile, $classPointer) : void {
        $this->linesCountBetweenMembers = SniffSettingsHelper::normalizeInteger($this->linesCountBetweenMembers);
        $tokens = $phpcsFile->getTokens();
        $memberPointer = null;
        $previousMemberPointer = null;
        do {
            $previousMemberPointer = $memberPointer;
            $memberPointer = $this->findNextMember($phpcsFile, $classPointer, $previousMemberPointer ?? $tokens[$classPointer]['scope_opener']);
            if ($memberPointer === null) {
                break;
            }
            if ($previousMemberPointer === null) {
                continue;
            }
            if ($tokens[$previousMemberPointer]['code'] === $tokens[$memberPointer]['code']) {
                continue;
            }
            $previousMemberEndPointer = $this->getMemberEndPointer($phpcsFile, $previousMemberPointer);
            $hasCommentWithNewLineAfterPreviousMember = false;
            $commentPointerAfterPreviousMember = TokenHelper::findNextNonWhitespace($phpcsFile, $previousMemberEndPointer + 1);
            if (in_array($tokens[$commentPointerAfterPreviousMember]['code'], TokenHelper::$inlineCommentTokenCodes, true) && ($tokens[$previousMemberEndPointer]['line'] === $tokens[$commentPointerAfterPreviousMember]['line'] || $tokens[$previousMemberEndPointer]['line'] + 1 === $tokens[$commentPointerAfterPreviousMember]['line'])) {
                $previousMemberEndPointer = CommentHelper::getCommentEndPointer($phpcsFile, $commentPointerAfterPreviousMember);
                if (StringHelper::endsWith($tokens[$commentPointerAfterPreviousMember]['content'], $phpcsFile->eolChar)) {
                    $hasCommentWithNewLineAfterPreviousMember = true;
                }
            }
            $memberStartPointer = $this->getMemberStartPointer($phpcsFile, $memberPointer);
            $actualLinesCount = $tokens[$memberStartPointer]['line'] - $tokens[$previousMemberEndPointer]['line'] - 1;
            if ($actualLinesCount === $this->linesCountBetweenMembers) {
                continue;
            }
            $errorMessage = $this->linesCountBetweenMembers === 1 ? 'Expected 1 blank line between class members, found %2$d.' : 'Expected %1$d blank lines between class members, found %2$d.';
            $fix = $phpcsFile->addFixableError(sprintf($errorMessage, $this->linesCountBetweenMembers, $actualLinesCount), $memberPointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS);
            if (!$fix) {
                continue;
            }
            $newLines = str_repeat($phpcsFile->eolChar, $this->linesCountBetweenMembers + ($hasCommentWithNewLineAfterPreviousMember ? 0 : 1));
            $firstPointerOnMemberLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $memberStartPointer);
            $phpcsFile->fixer
                ->beginChangeset();
            $phpcsFile->fixer
                ->addContent($previousMemberEndPointer, $newLines);
            FixerHelper::removeBetween($phpcsFile, $previousMemberEndPointer, $firstPointerOnMemberLine);
            $phpcsFile->fixer
                ->endChangeset();
        } while (true);
    }
    private function findNextMember(File $phpcsFile, int $classPointer, int $previousMemberPointer) : ?int {
        $tokens = $phpcsFile->getTokens();
        $memberPointer = $previousMemberPointer;
        do {
            $memberPointer = TokenHelper::findNext($phpcsFile, [
                T_USE,
                T_CONST,
                T_VAR,
                T_PUBLIC,
                T_PROTECTED,
                T_PRIVATE,
                T_READONLY,
                T_STATIC,
                T_FUNCTION,
                T_ENUM_CASE,
            ], $memberPointer + 1, $tokens[$classPointer]['scope_closer']);
            if ($memberPointer === null) {
                return null;
            }
            if ($tokens[$memberPointer]['code'] === T_USE) {
                if (!UseStatementHelper::isTraitUse($phpcsFile, $memberPointer)) {
                    continue;
                }
            }
            elseif (in_array($tokens[$memberPointer]['code'], [
                T_VAR,
                T_PUBLIC,
                T_PROTECTED,
                T_PRIVATE,
                T_READONLY,
                T_STATIC,
            ], true)) {
                $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $memberPointer - 1);
                if ($tokens[$asPointer]['code'] === T_AS) {
                    continue;
                }
                $propertyPointer = TokenHelper::findNext($phpcsFile, [
                    T_VARIABLE,
                    T_FUNCTION,
                    T_CONST,
                    T_ENUM_CASE,
                ], $memberPointer + 1);
                if ($propertyPointer === null || $tokens[$propertyPointer]['code'] !== T_VARIABLE || !PropertyHelper::isProperty($phpcsFile, $propertyPointer)) {
                    continue;
                }
                $memberPointer = $propertyPointer;
            }
            if (ScopeHelper::isInSameScope($phpcsFile, $memberPointer, $previousMemberPointer)) {
                break;
            }
        } while (true);
        return $memberPointer;
    }
    private function getMemberStartPointer(File $phpcsFile, int $memberPointer) : int {
        $tokens = $phpcsFile->getTokens();
        $memberFirstCodePointer = $this->getMemberFirstCodePointer($phpcsFile, $memberPointer);
        do {
            $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $memberFirstCodePointer - 1);
            if ($tokens[$pointerBefore]['code'] === T_ATTRIBUTE_END) {
                $memberFirstCodePointer = $tokens[$pointerBefore]['attribute_opener'];
                continue;
            }
            if (in_array($tokens[$pointerBefore]['code'], Tokens::$commentTokens, true) && $tokens[$pointerBefore]['line'] + 1 === $tokens[$memberFirstCodePointer]['line']) {
                $pointerBeforeComment = TokenHelper::findPreviousEffective($phpcsFile, $pointerBefore - 1);
                if ($tokens[$pointerBeforeComment]['line'] !== $tokens[$pointerBefore]['line']) {
                    $memberFirstCodePointer = array_key_exists('comment_opener', $tokens[$pointerBefore]) ? $tokens[$pointerBefore]['comment_opener'] : CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBefore);
                    continue;
                }
            }
            break;
        } while (true);
        return $memberFirstCodePointer;
    }
    private function getMemberFirstCodePointer(File $phpcsFile, int $memberPointer) : int {
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$memberPointer]['code'] === T_USE) {
            return $memberPointer;
        }
        $firstCodePointer = $memberPointer;
        $previousFirstCodePointer = $memberPointer;
        do {
            
            /** @var int $firstCodePointer */
            $firstCodePointer = TokenHelper::findPrevious($phpcsFile, [
                T_VAR,
                T_PUBLIC,
                T_PROTECTED,
                T_PRIVATE,
                T_ABSTRACT,
                T_FINAL,
                T_SEMICOLON,
                T_CLOSE_CURLY_BRACKET,
            ], $firstCodePointer - 1);
            if (in_array($tokens[$firstCodePointer]['code'], [
                T_SEMICOLON,
                T_CLOSE_CURLY_BRACKET,
            ], true)) {
                break;
            }
            $previousFirstCodePointer = $firstCodePointer;
        } while (true);
        return $previousFirstCodePointer;
    }
    private function getMemberEndPointer(File $phpcsFile, int $memberPointer) : int {
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$memberPointer]['code'] === T_USE) {
            $pointer = TokenHelper::findNextLocal($phpcsFile, [
                T_SEMICOLON,
                T_OPEN_CURLY_BRACKET,
            ], $memberPointer + 1);
            return $tokens[$pointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$pointer]['bracket_closer'] : $pointer;
        }
        if ($tokens[$memberPointer]['code'] === T_FUNCTION && !FunctionHelper::isAbstract($phpcsFile, $memberPointer)) {
            return $tokens[$memberPointer]['scope_closer'];
        }
        return TokenHelper::findNext($phpcsFile, T_SEMICOLON, $memberPointer + 1);
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
ClassMemberSpacingSniff::$linesCountBetweenMembers public property @var int
ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS public constant
ClassMemberSpacingSniff::findNextMember private function
ClassMemberSpacingSniff::getMemberEndPointer private function
ClassMemberSpacingSniff::getMemberFirstCodePointer private function
ClassMemberSpacingSniff::getMemberStartPointer private function
ClassMemberSpacingSniff::process public function * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*
Overrides Sniff::process
ClassMemberSpacingSniff::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