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

Breadcrumb

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

class ClassStructureSniff

Hierarchy

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

Expanded class hierarchy of ClassStructureSniff

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassStructureSniff.php, line 45

Namespace

SlevomatCodingStandard\Sniffs\Classes
View source
class ClassStructureSniff implements Sniff {
    public const CODE_INCORRECT_GROUP_ORDER = 'IncorrectGroupOrder';
    private const GROUP_USES = 'uses';
    private const GROUP_PUBLIC_CONSTANTS = 'public constants';
    private const GROUP_PROTECTED_CONSTANTS = 'protected constants';
    private const GROUP_PRIVATE_CONSTANTS = 'private constants';
    private const GROUP_PUBLIC_PROPERTIES = 'public properties';
    private const GROUP_PUBLIC_STATIC_PROPERTIES = 'public static properties';
    private const GROUP_PROTECTED_PROPERTIES = 'protected properties';
    private const GROUP_PROTECTED_STATIC_PROPERTIES = 'protected static properties';
    private const GROUP_PRIVATE_PROPERTIES = 'private properties';
    private const GROUP_PRIVATE_STATIC_PROPERTIES = 'private static properties';
    private const GROUP_CONSTRUCTOR = 'constructor';
    private const GROUP_STATIC_CONSTRUCTORS = 'static constructors';
    private const GROUP_DESTRUCTOR = 'destructor';
    private const GROUP_MAGIC_METHODS = 'magic methods';
    private const GROUP_PUBLIC_METHODS = 'public methods';
    private const GROUP_PUBLIC_ABSTRACT_METHODS = 'public abstract methods';
    private const GROUP_PUBLIC_FINAL_METHODS = 'public final methods';
    private const GROUP_PUBLIC_STATIC_METHODS = 'public static methods';
    private const GROUP_PUBLIC_STATIC_ABSTRACT_METHODS = 'public static abstract methods';
    private const GROUP_PUBLIC_STATIC_FINAL_METHODS = 'public static final methods';
    private const GROUP_PROTECTED_METHODS = 'protected methods';
    private const GROUP_PROTECTED_ABSTRACT_METHODS = 'protected abstract methods';
    private const GROUP_PROTECTED_FINAL_METHODS = 'protected final methods';
    private const GROUP_PROTECTED_STATIC_METHODS = 'protected static methods';
    private const GROUP_PROTECTED_STATIC_ABSTRACT_METHODS = 'protected static abstract methods';
    private const GROUP_PROTECTED_STATIC_FINAL_METHODS = 'protected static final methods';
    private const GROUP_PRIVATE_METHODS = 'private methods';
    private const GROUP_PRIVATE_STATIC_METHODS = 'private static methods';
    private const GROUP_ENUM_CASES = 'enum cases';
    private const GROUP_SHORTCUT_CONSTANTS = 'constants';
    private const GROUP_SHORTCUT_PROPERTIES = 'properties';
    private const GROUP_SHORTCUT_STATIC_PROPERTIES = 'static properties';
    private const GROUP_SHORTCUT_METHODS = 'methods';
    private const GROUP_SHORTCUT_PUBLIC_METHODS = 'all public methods';
    private const GROUP_SHORTCUT_PROTECTED_METHODS = 'all protected methods';
    private const GROUP_SHORTCUT_PRIVATE_METHODS = 'all private methods';
    private const GROUP_SHORTCUT_STATIC_METHODS = 'static methods';
    private const GROUP_SHORTCUT_ABSTRACT_METHODS = 'abstract methods';
    private const GROUP_SHORTCUT_FINAL_METHODS = 'final methods';
    private const SHORTCUTS = [
        self::GROUP_SHORTCUT_CONSTANTS => [
            self::GROUP_PUBLIC_CONSTANTS,
            self::GROUP_PROTECTED_CONSTANTS,
            self::GROUP_PRIVATE_CONSTANTS,
        ],
        self::GROUP_SHORTCUT_STATIC_PROPERTIES => [
            self::GROUP_PUBLIC_STATIC_PROPERTIES,
            self::GROUP_PROTECTED_STATIC_PROPERTIES,
            self::GROUP_PRIVATE_STATIC_PROPERTIES,
        ],
        self::GROUP_SHORTCUT_PROPERTIES => [
            self::GROUP_SHORTCUT_STATIC_PROPERTIES,
            self::GROUP_PUBLIC_PROPERTIES,
            self::GROUP_PROTECTED_PROPERTIES,
            self::GROUP_PRIVATE_PROPERTIES,
        ],
        self::GROUP_SHORTCUT_PUBLIC_METHODS => [
            self::GROUP_PUBLIC_FINAL_METHODS,
            self::GROUP_PUBLIC_STATIC_FINAL_METHODS,
            self::GROUP_PUBLIC_ABSTRACT_METHODS,
            self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS,
            self::GROUP_PUBLIC_STATIC_METHODS,
            self::GROUP_PUBLIC_METHODS,
        ],
        self::GROUP_SHORTCUT_PROTECTED_METHODS => [
            self::GROUP_PROTECTED_FINAL_METHODS,
            self::GROUP_PROTECTED_STATIC_FINAL_METHODS,
            self::GROUP_PROTECTED_ABSTRACT_METHODS,
            self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS,
            self::GROUP_PROTECTED_STATIC_METHODS,
            self::GROUP_PROTECTED_METHODS,
        ],
        self::GROUP_SHORTCUT_PRIVATE_METHODS => [
            self::GROUP_PRIVATE_STATIC_METHODS,
            self::GROUP_PRIVATE_METHODS,
        ],
        self::GROUP_SHORTCUT_FINAL_METHODS => [
            self::GROUP_PUBLIC_FINAL_METHODS,
            self::GROUP_PROTECTED_FINAL_METHODS,
            self::GROUP_PUBLIC_STATIC_FINAL_METHODS,
            self::GROUP_PROTECTED_STATIC_FINAL_METHODS,
        ],
        self::GROUP_SHORTCUT_ABSTRACT_METHODS => [
            self::GROUP_PUBLIC_ABSTRACT_METHODS,
            self::GROUP_PROTECTED_ABSTRACT_METHODS,
            self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS,
            self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS,
        ],
        self::GROUP_SHORTCUT_STATIC_METHODS => [
            self::GROUP_STATIC_CONSTRUCTORS,
            self::GROUP_PUBLIC_STATIC_FINAL_METHODS,
            self::GROUP_PROTECTED_STATIC_FINAL_METHODS,
            self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS,
            self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS,
            self::GROUP_PUBLIC_STATIC_METHODS,
            self::GROUP_PROTECTED_STATIC_METHODS,
            self::GROUP_PRIVATE_STATIC_METHODS,
        ],
        self::GROUP_SHORTCUT_METHODS => [
            self::GROUP_SHORTCUT_FINAL_METHODS,
            self::GROUP_SHORTCUT_ABSTRACT_METHODS,
            self::GROUP_SHORTCUT_STATIC_METHODS,
            self::GROUP_CONSTRUCTOR,
            self::GROUP_DESTRUCTOR,
            self::GROUP_PUBLIC_METHODS,
            self::GROUP_PROTECTED_METHODS,
            self::GROUP_PRIVATE_METHODS,
            self::GROUP_MAGIC_METHODS,
        ],
    ];
    private const SPECIAL_METHODS = [
        '__construct' => self::GROUP_CONSTRUCTOR,
        '__destruct' => self::GROUP_DESTRUCTOR,
        '__call' => self::GROUP_MAGIC_METHODS,
        '__callstatic' => self::GROUP_MAGIC_METHODS,
        '__get' => self::GROUP_MAGIC_METHODS,
        '__set' => self::GROUP_MAGIC_METHODS,
        '__isset' => self::GROUP_MAGIC_METHODS,
        '__unset' => self::GROUP_MAGIC_METHODS,
        '__sleep' => self::GROUP_MAGIC_METHODS,
        '__wakeup' => self::GROUP_MAGIC_METHODS,
        '__serialize' => self::GROUP_MAGIC_METHODS,
        '__unserialize' => self::GROUP_MAGIC_METHODS,
        '__tostring' => self::GROUP_MAGIC_METHODS,
        '__invoke' => self::GROUP_MAGIC_METHODS,
        '__set_state' => self::GROUP_MAGIC_METHODS,
        '__clone' => self::GROUP_MAGIC_METHODS,
        '__debuginfo' => self::GROUP_MAGIC_METHODS,
    ];
    
    /** @var list<string> */
    public $groups = [];
    
    /** @var array<string, int>|null */
    private $normalizedGroups;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        return array_values(Tokens::$ooScopeTokens);
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $pointer
     */
    public function process(File $phpcsFile, $pointer) : int {
        $tokens = $phpcsFile->getTokens();
        
        /** @var array{scope_closer: int, level: int} $rootScopeToken */
        $rootScopeToken = $tokens[$pointer];
        $groupsOrder = $this->getNormalizedGroups();
        $groupLastMemberPointer = $rootScopeToken['scope_opener'];
        $expectedGroup = null;
        $groupsFirstMembers = [];
        while (true) {
            $nextGroup = $this->findNextGroup($phpcsFile, $groupLastMemberPointer, $rootScopeToken);
            if ($nextGroup === null) {
                break;
            }
            [
                $groupFirstMemberPointer,
                $groupLastMemberPointer,
                $group,
            ] = $nextGroup;
            if ($groupsOrder[$group] >= ($groupsOrder[$expectedGroup] ?? 0)) {
                $groupsFirstMembers[$group] = $groupFirstMemberPointer;
                $expectedGroup = $group;
                continue;
            }
            $expectedGroups = array_filter($groupsOrder, static function (int $order) use ($groupsOrder, $expectedGroup) : bool {
                return $order >= $groupsOrder[$expectedGroup];
            });
            $fix = $phpcsFile->addFixableError(sprintf('The placement of "%s" group is invalid. Last group was "%s" and one of these is expected after it: %s', $group, $expectedGroup, implode(', ', array_keys($expectedGroups))), $groupFirstMemberPointer, self::CODE_INCORRECT_GROUP_ORDER);
            if (!$fix) {
                continue;
            }
            foreach ($groupsFirstMembers as $memberGroup => $firstMemberPointer) {
                if ($groupsOrder[$memberGroup] <= $groupsOrder[$group]) {
                    continue;
                }
                $this->fixIncorrectGroupOrder($phpcsFile, $groupFirstMemberPointer, $groupLastMemberPointer, $firstMemberPointer);
                // run the sniff again to fix the rest of the groups
                return $pointer - 1;
            }
        }
        return $pointer + 1;
    }
    
    /**
     * @param array{scope_closer: int, level: int} $rootScopeToken
     * @return array{int, int, string}|null
     */
    private function findNextGroup(File $phpcsFile, int $pointer, array $rootScopeToken) : ?array {
        $tokens = $phpcsFile->getTokens();
        $groupTokenTypes = [
            T_USE,
            T_ENUM_CASE,
            T_CONST,
            T_VARIABLE,
            T_FUNCTION,
        ];
        $currentTokenPointer = $pointer;
        while (true) {
            $currentTokenPointer = TokenHelper::findNext($phpcsFile, $groupTokenTypes, ($currentToken['scope_closer'] ?? $currentTokenPointer) + 1, $rootScopeToken['scope_closer']);
            if ($currentTokenPointer === null) {
                break;
            }
            $currentToken = $tokens[$currentTokenPointer];
            if ($currentToken['code'] === T_VARIABLE && !PropertyHelper::isProperty($phpcsFile, $currentTokenPointer)) {
                continue;
            }
            if ($currentToken['level'] - $rootScopeToken['level'] !== 1) {
                continue;
            }
            $group = $this->getGroupForToken($phpcsFile, $currentTokenPointer);
            if (!isset($currentGroup)) {
                $currentGroup = $group;
                $groupFirstMemberPointer = $currentTokenPointer;
            }
            if ($group !== $currentGroup) {
                break;
            }
            $groupLastMemberPointer = $currentTokenPointer;
        }
        if (!isset($currentGroup)) {
            return null;
        }
        assert(isset($groupFirstMemberPointer) === true);
        assert(isset($groupLastMemberPointer) === true);
        return [
            $groupFirstMemberPointer,
            $groupLastMemberPointer,
            $currentGroup,
        ];
    }
    private function getGroupForToken(File $phpcsFile, int $pointer) : string {
        $tokens = $phpcsFile->getTokens();
        switch ($tokens[$pointer]['code']) {
            case T_USE:
                return self::GROUP_USES;
            case T_ENUM_CASE:
                return self::GROUP_ENUM_CASES;
            case T_CONST:
                switch ($this->getVisibilityForToken($phpcsFile, $pointer)) {
                    case T_PUBLIC:
                        return self::GROUP_PUBLIC_CONSTANTS;
                    case T_PROTECTED:
                        return self::GROUP_PROTECTED_CONSTANTS;
                }
                return self::GROUP_PRIVATE_CONSTANTS;
            case T_FUNCTION:
                $name = strtolower(FunctionHelper::getName($phpcsFile, $pointer));
                if (array_key_exists($name, self::SPECIAL_METHODS)) {
                    return self::SPECIAL_METHODS[$name];
                }
                $visibility = $this->getVisibilityForToken($phpcsFile, $pointer);
                $isStatic = $this->isMemberStatic($phpcsFile, $pointer);
                $isFinal = $this->isMethodFinal($phpcsFile, $pointer);
                if ($this->isMethodAbstract($phpcsFile, $pointer)) {
                    if ($visibility === T_PUBLIC) {
                        return $isStatic ? self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS : self::GROUP_PUBLIC_ABSTRACT_METHODS;
                    }
                    return $isStatic ? self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS : self::GROUP_PROTECTED_ABSTRACT_METHODS;
                }
                if ($isStatic && $visibility === T_PUBLIC && $this->isStaticConstructor($phpcsFile, $pointer)) {
                    return self::GROUP_STATIC_CONSTRUCTORS;
                }
                switch ($visibility) {
                    case T_PUBLIC:
                        if ($isFinal) {
                            return $isStatic ? self::GROUP_PUBLIC_STATIC_FINAL_METHODS : self::GROUP_PUBLIC_FINAL_METHODS;
                        }
                        return $isStatic ? self::GROUP_PUBLIC_STATIC_METHODS : self::GROUP_PUBLIC_METHODS;
                    case T_PROTECTED:
                        if ($isFinal) {
                            return $isStatic ? self::GROUP_PROTECTED_STATIC_FINAL_METHODS : self::GROUP_PROTECTED_FINAL_METHODS;
                        }
                        return $isStatic ? self::GROUP_PROTECTED_STATIC_METHODS : self::GROUP_PROTECTED_METHODS;
                }
                return $isStatic ? self::GROUP_PRIVATE_STATIC_METHODS : self::GROUP_PRIVATE_METHODS;
            default:
                $isStatic = $this->isMemberStatic($phpcsFile, $pointer);
                $visibility = $this->getVisibilityForToken($phpcsFile, $pointer);
                switch ($visibility) {
                    case T_PUBLIC:
                        return $isStatic ? self::GROUP_PUBLIC_STATIC_PROPERTIES : self::GROUP_PUBLIC_PROPERTIES;
                    case T_PROTECTED:
                        return $isStatic ? self::GROUP_PROTECTED_STATIC_PROPERTIES : self::GROUP_PROTECTED_PROPERTIES;
                    default:
                        return $isStatic ? self::GROUP_PRIVATE_STATIC_PROPERTIES : self::GROUP_PRIVATE_PROPERTIES;
                }
        }
    }
    private function getVisibilityForToken(File $phpcsFile, int $pointer) : int {
        $tokens = $phpcsFile->getTokens();
        $previousPointer = TokenHelper::findPrevious($phpcsFile, array_merge(Tokens::$scopeModifiers, [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
        ]), $pointer - 1);
        
        /** @var int $visibilityPointer */
        $visibilityPointer = in_array($tokens[$previousPointer]['code'], Tokens::$scopeModifiers, true) ? $tokens[$previousPointer]['code'] : T_PUBLIC;
        return $visibilityPointer;
    }
    private function isMemberStatic(File $phpcsFile, int $pointer) : bool {
        $previousPointer = TokenHelper::findPrevious($phpcsFile, [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
            T_STATIC,
        ], $pointer - 1);
        return $phpcsFile->getTokens()[$previousPointer]['code'] === T_STATIC;
    }
    private function isMethodFinal(File $phpcsFile, int $pointer) : bool {
        $previousPointer = TokenHelper::findPrevious($phpcsFile, [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
            T_FINAL,
        ], $pointer - 1);
        return $phpcsFile->getTokens()[$previousPointer]['code'] === T_FINAL;
    }
    private function isMethodAbstract(File $phpcsFile, int $pointer) : bool {
        $previousPointer = TokenHelper::findPrevious($phpcsFile, [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
            T_ABSTRACT,
        ], $pointer - 1);
        return $phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT;
    }
    private function isStaticConstructor(File $phpcsFile, int $pointer) : bool {
        $parentClassName = $this->getParentClassName($phpcsFile, $pointer);
        $returnTypeHint = FunctionHelper::findReturnTypeHint($phpcsFile, $pointer);
        if ($returnTypeHint !== null) {
            return in_array($returnTypeHint->getTypeHintWithoutNullabilitySymbol(), [
                'self',
                $parentClassName,
            ], true);
        }
        $returnAnnotation = FunctionHelper::findReturnAnnotation($phpcsFile, $pointer);
        if ($returnAnnotation === null) {
            return false;
        }
        return in_array((string) $returnAnnotation->getValue()->type, [
            'static',
            'self',
            $parentClassName,
        ], true);
    }
    private function getParentClassName(File $phpcsFile, int $pointer) : string {
        $classPointer = TokenHelper::findPrevious($phpcsFile, Tokens::$ooScopeTokens, $pointer - 1);
        assert($classPointer !== null);
        return ClassHelper::getName($phpcsFile, $classPointer);
    }
    private function fixIncorrectGroupOrder(File $file, int $groupFirstMemberPointer, int $groupLastMemberPointer, int $nextGroupMemberPointer) : void {
        $previousMemberEndPointer = $this->findPreviousMemberEndPointer($file, $groupFirstMemberPointer);
        $groupStartPointer = $this->findGroupStartPointer($file, $groupFirstMemberPointer, $previousMemberEndPointer);
        $groupEndPointer = $this->findGroupEndPointer($file, $groupLastMemberPointer);
        $groupContent = TokenHelper::getContent($file, $groupStartPointer, $groupEndPointer);
        $nextGroupMemberStartPointer = $this->findGroupStartPointer($file, $nextGroupMemberPointer);
        $file->fixer
            ->beginChangeset();
        FixerHelper::removeBetweenIncluding($file, $groupStartPointer, $groupEndPointer);
        $linesBetween = $this->removeBlankLinesAfterMember($file, $previousMemberEndPointer, $groupStartPointer);
        $newLines = str_repeat($file->eolChar, $linesBetween);
        $file->fixer
            ->addContentBefore($nextGroupMemberStartPointer, $groupContent . $newLines);
        $file->fixer
            ->endChangeset();
    }
    private function findPreviousMemberEndPointer(File $phpcsFile, int $memberPointer) : int {
        $endTypes = [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
        ];
        $previousMemberEndPointer = TokenHelper::findPrevious($phpcsFile, $endTypes, $memberPointer - 1);
        assert($previousMemberEndPointer !== null);
        return $previousMemberEndPointer;
    }
    private function findGroupStartPointer(File $phpcsFile, int $memberPointer, ?int $previousMemberEndPointer = null) : int {
        $startPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $memberPointer - 1);
        if ($startPointer === null) {
            if ($previousMemberEndPointer === null) {
                $previousMemberEndPointer = $this->findPreviousMemberEndPointer($phpcsFile, $memberPointer);
            }
            $startPointer = TokenHelper::findNextEffective($phpcsFile, $previousMemberEndPointer + 1);
            assert($startPointer !== null);
        }
        $types = [
            T_OPEN_CURLY_BRACKET,
            T_CLOSE_CURLY_BRACKET,
            T_SEMICOLON,
        ];
        return (int) $phpcsFile->findFirstOnLine($types, $startPointer, true);
    }
    private function findGroupEndPointer(File $phpcsFile, int $memberPointer) : int {
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$memberPointer]['code'] === T_FUNCTION && !FunctionHelper::isAbstract($phpcsFile, $memberPointer)) {
            $endPointer = $tokens[$memberPointer]['scope_closer'];
        }
        elseif ($tokens[$memberPointer]['code'] === T_USE && array_key_exists('scope_closer', $tokens[$memberPointer])) {
            $endPointer = $tokens[$memberPointer]['scope_closer'];
        }
        else {
            $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $memberPointer + 1);
            assert($endPointer !== null);
        }
        return $endPointer;
    }
    private function removeBlankLinesAfterMember(File $phpcsFile, int $memberEndPointer, int $endPointer) : int {
        $whitespacePointer = $memberEndPointer;
        $linesToRemove = 0;
        while (true) {
            $whitespacePointer = TokenHelper::findNext($phpcsFile, T_WHITESPACE, $whitespacePointer, $endPointer);
            if ($whitespacePointer === null) {
                break;
            }
            $linesToRemove++;
            $phpcsFile->fixer
                ->replaceToken($whitespacePointer, '');
            $whitespacePointer++;
        }
        return $linesToRemove;
    }
    
    /**
     * @return array<string, int>
     */
    private function getNormalizedGroups() : array {
        if ($this->normalizedGroups === null) {
            $supportedGroups = [
                self::GROUP_USES,
                self::GROUP_ENUM_CASES,
                self::GROUP_PUBLIC_CONSTANTS,
                self::GROUP_PROTECTED_CONSTANTS,
                self::GROUP_PRIVATE_CONSTANTS,
                self::GROUP_PUBLIC_PROPERTIES,
                self::GROUP_PUBLIC_STATIC_PROPERTIES,
                self::GROUP_PROTECTED_PROPERTIES,
                self::GROUP_PROTECTED_STATIC_PROPERTIES,
                self::GROUP_PRIVATE_PROPERTIES,
                self::GROUP_PRIVATE_STATIC_PROPERTIES,
                self::GROUP_PUBLIC_STATIC_FINAL_METHODS,
                self::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS,
                self::GROUP_PROTECTED_STATIC_FINAL_METHODS,
                self::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS,
                self::GROUP_PUBLIC_FINAL_METHODS,
                self::GROUP_PUBLIC_ABSTRACT_METHODS,
                self::GROUP_PROTECTED_FINAL_METHODS,
                self::GROUP_PROTECTED_ABSTRACT_METHODS,
                self::GROUP_CONSTRUCTOR,
                self::GROUP_STATIC_CONSTRUCTORS,
                self::GROUP_DESTRUCTOR,
                self::GROUP_PUBLIC_METHODS,
                self::GROUP_PUBLIC_STATIC_METHODS,
                self::GROUP_PROTECTED_METHODS,
                self::GROUP_PROTECTED_STATIC_METHODS,
                self::GROUP_PRIVATE_METHODS,
                self::GROUP_PRIVATE_STATIC_METHODS,
                self::GROUP_MAGIC_METHODS,
            ];
            $normalizedGroupsWithShortcuts = [];
            $order = 1;
            foreach (SniffSettingsHelper::normalizeArray($this->groups) as $groupsString) {
                
                /** @var list<string> $groups */
                $groups = preg_split('~\\s*,\\s*~', strtolower($groupsString));
                foreach ($groups as $groupOrShortcut) {
                    $groupOrShortcut = preg_replace('~\\s+~', ' ', $groupOrShortcut);
                    if (!in_array($groupOrShortcut, $supportedGroups, true) && !array_key_exists($groupOrShortcut, self::SHORTCUTS)) {
                        throw new UnsupportedClassGroupException($groupOrShortcut);
                    }
                    $normalizedGroupsWithShortcuts[$groupOrShortcut] = $order;
                }
                $order++;
            }
            $normalizedGroups = [];
            foreach ($normalizedGroupsWithShortcuts as $groupOrShortcut => $groupOrder) {
                if (in_array($groupOrShortcut, $supportedGroups, true)) {
                    $normalizedGroups[$groupOrShortcut] = $groupOrder;
                }
                else {
                    foreach ($this->unpackShortcut($groupOrShortcut, $supportedGroups) as $group) {
                        if (array_key_exists($group, $normalizedGroupsWithShortcuts) || array_key_exists($group, $normalizedGroups)) {
                            continue;
                        }
                        $normalizedGroups[$group] = $groupOrder;
                    }
                }
            }
            if ($normalizedGroups === []) {
                $normalizedGroups = array_flip($supportedGroups);
            }
            else {
                $missingGroups = array_diff($supportedGroups, array_keys($normalizedGroups));
                if ($missingGroups !== []) {
                    throw new MissingClassGroupsException($missingGroups);
                }
            }
            $this->normalizedGroups = $normalizedGroups;
        }
        return $this->normalizedGroups;
    }
    
    /**
     * @param array<int, string> $supportedGroups
     * @return array<int, string>
     */
    private function unpackShortcut(string $shortcut, array $supportedGroups) : array {
        $groups = [];
        foreach (self::SHORTCUTS[$shortcut] as $groupOrShortcut) {
            if (in_array($groupOrShortcut, $supportedGroups, true)) {
                $groups[] = $groupOrShortcut;
            }
            elseif (!array_key_exists($groupOrShortcut, self::SHORTCUTS) && in_array($groupOrShortcut, self::SHORTCUTS[self::GROUP_SHORTCUT_FINAL_METHODS], true)) {
                // Nothing
            }
            else {
                $groups = array_merge($groups, $this->unpackShortcut($groupOrShortcut, $supportedGroups));
            }
        }
        return $groups;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
ClassStructureSniff::$groups public property @var list&lt;string&gt;
ClassStructureSniff::$normalizedGroups private property @var array&lt;string, int&gt;|null
ClassStructureSniff::CODE_INCORRECT_GROUP_ORDER public constant
ClassStructureSniff::findGroupEndPointer private function
ClassStructureSniff::findGroupStartPointer private function
ClassStructureSniff::findNextGroup private function *
ClassStructureSniff::findPreviousMemberEndPointer private function
ClassStructureSniff::fixIncorrectGroupOrder private function
ClassStructureSniff::getGroupForToken private function
ClassStructureSniff::getNormalizedGroups private function *
ClassStructureSniff::getParentClassName private function
ClassStructureSniff::getVisibilityForToken private function
ClassStructureSniff::GROUP_CONSTRUCTOR private constant
ClassStructureSniff::GROUP_DESTRUCTOR private constant
ClassStructureSniff::GROUP_ENUM_CASES private constant
ClassStructureSniff::GROUP_MAGIC_METHODS private constant
ClassStructureSniff::GROUP_PRIVATE_CONSTANTS private constant
ClassStructureSniff::GROUP_PRIVATE_METHODS private constant
ClassStructureSniff::GROUP_PRIVATE_PROPERTIES private constant
ClassStructureSniff::GROUP_PRIVATE_STATIC_METHODS private constant
ClassStructureSniff::GROUP_PRIVATE_STATIC_PROPERTIES private constant
ClassStructureSniff::GROUP_PROTECTED_ABSTRACT_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_CONSTANTS private constant
ClassStructureSniff::GROUP_PROTECTED_FINAL_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_PROPERTIES private constant
ClassStructureSniff::GROUP_PROTECTED_STATIC_ABSTRACT_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_STATIC_FINAL_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_STATIC_METHODS private constant
ClassStructureSniff::GROUP_PROTECTED_STATIC_PROPERTIES private constant
ClassStructureSniff::GROUP_PUBLIC_ABSTRACT_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_CONSTANTS private constant
ClassStructureSniff::GROUP_PUBLIC_FINAL_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_PROPERTIES private constant
ClassStructureSniff::GROUP_PUBLIC_STATIC_ABSTRACT_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_STATIC_FINAL_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_STATIC_METHODS private constant
ClassStructureSniff::GROUP_PUBLIC_STATIC_PROPERTIES private constant
ClassStructureSniff::GROUP_SHORTCUT_ABSTRACT_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_CONSTANTS private constant
ClassStructureSniff::GROUP_SHORTCUT_FINAL_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_PRIVATE_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_PROPERTIES private constant
ClassStructureSniff::GROUP_SHORTCUT_PROTECTED_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_PUBLIC_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_STATIC_METHODS private constant
ClassStructureSniff::GROUP_SHORTCUT_STATIC_PROPERTIES private constant
ClassStructureSniff::GROUP_STATIC_CONSTRUCTORS private constant
ClassStructureSniff::GROUP_USES private constant
ClassStructureSniff::isMemberStatic private function
ClassStructureSniff::isMethodAbstract private function
ClassStructureSniff::isMethodFinal private function
ClassStructureSniff::isStaticConstructor private function
ClassStructureSniff::process public function * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*
Overrides Sniff::process
ClassStructureSniff::register public function * Overrides Sniff::register
ClassStructureSniff::removeBlankLinesAfterMember private function
ClassStructureSniff::SHORTCUTS private constant
ClassStructureSniff::SPECIAL_METHODS private constant
ClassStructureSniff::unpackShortcut private function *

API Navigation

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