?????????? ????????? - ??????????????? - /opt/cpanel/ea-wappspector/vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Classes/ClassStructureSniff.php
???????
<?php declare(strict_types = 1); namespace SlevomatCodingStandard\Sniffs\Classes; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Util\Tokens; use SlevomatCodingStandard\Helpers\AnnotationHelper; use SlevomatCodingStandard\Helpers\AttributeHelper; use SlevomatCodingStandard\Helpers\ClassHelper; use SlevomatCodingStandard\Helpers\DocCommentHelper; use SlevomatCodingStandard\Helpers\FixerHelper; use SlevomatCodingStandard\Helpers\FunctionHelper; use SlevomatCodingStandard\Helpers\PropertyHelper; use SlevomatCodingStandard\Helpers\SniffSettingsHelper; use SlevomatCodingStandard\Helpers\StringHelper; use SlevomatCodingStandard\Helpers\TokenHelper; use function array_diff; use function array_filter; use function array_flip; use function array_key_exists; use function array_keys; use function array_merge; use function array_shift; use function array_values; use function assert; use function implode; use function in_array; use function ltrim; use function preg_replace; use function preg_split; use function sprintf; use function str_repeat; use function strtolower; use function substr; use const PREG_SPLIT_NO_EMPTY; use const T_ABSTRACT; use const T_CLOSE_CURLY_BRACKET; use const T_CONST; use const T_ENUM_CASE; use const T_FINAL; use const T_FUNCTION; use const T_OPEN_CURLY_BRACKET; use const T_PRIVATE_SET; use const T_PROTECTED; use const T_PROTECTED_SET; use const T_PUBLIC; use const T_PUBLIC_SET; use const T_SEMICOLON; use const T_STATIC; use const T_USE; use const T_VARIABLE; use const T_WHITESPACE; 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_INVOKE_METHOD = 'invoke method'; 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_INVOKE_METHOD, '__set_state' => self::GROUP_MAGIC_METHODS, '__clone' => self::GROUP_MAGIC_METHODS, '__debuginfo' => self::GROUP_MAGIC_METHODS, ]; /** @var array<string, string> */ public array $methodGroups = []; /** @var list<string> */ public array $groups = []; /** @var array<string, list<array{name: string|null, attributes: array<string>, annotations: array<string>}>>|null */ private ?array $normalizedMethodGroups = null; /** @var array<string, int>|null */ private ?array $normalizedGroups = null; /** * @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(); $rootScopeToken = $tokens[$pointer]; assert(array_key_exists('scope_opener', $rootScopeToken)); $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; // Use "magic methods" group for __invoke() when "invoke" group is not explicitly defined if ($group === self::GROUP_INVOKE_METHOD && !array_key_exists($group, $groupsOrder)) { $group = self::GROUP_MAGIC_METHODS; } if ($groupsOrder[$group] >= ($expectedGroup !== null ? $groupsOrder[$expectedGroup] : 0)) { $groupsFirstMembers[$group] = $groupFirstMemberPointer; $expectedGroup = $group; continue; } $expectedGroups = array_filter( $groupsOrder, static fn (int $order): bool => $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(); $currentTokenPointer = $pointer; while (true) { $currentTokenPointer = TokenHelper::findNext( $phpcsFile, [T_USE, T_ENUM_CASE, T_CONST, T_VARIABLE, T_FUNCTION], $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; $currentTokenPointer = $currentToken['code'] === T_VARIABLE // Skip to the end of the property definition ? PropertyHelper::getEndPointer($phpcsFile, $currentTokenPointer) : ($currentToken['scope_closer'] ?? $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]; } $methodGroup = $this->resolveMethodGroup($phpcsFile, $pointer, $name); if ($methodGroup !== null) { return $methodGroup; } $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: case T_PUBLIC_SET: 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 resolveMethodGroup(File $phpcsFile, int $pointer, string $method): ?string { foreach ($this->getNormalizedMethodGroups() as $group => $methodRequirements) { foreach ($methodRequirements as $methodRequirement) { if ($methodRequirement['name'] !== null) { $requiredName = strtolower($methodRequirement['name']); if (StringHelper::endsWith($requiredName, '*')) { $methodNamePrefix = substr($requiredName, 0, -1); if ($method === $methodNamePrefix || !StringHelper::startsWith($method, $methodNamePrefix)) { continue; } } elseif ($method !== $requiredName) { continue; } } if ( $this->hasRequiredAnnotations($phpcsFile, $pointer, $methodRequirement['annotations']) && $this->hasRequiredAttributes($phpcsFile, $pointer, $methodRequirement['attributes']) ) { return $group; } } } return null; } /** * @param array<string> $requiredAnnotations */ private function hasRequiredAnnotations(File $phpcsFile, int $pointer, array $requiredAnnotations): bool { if ($requiredAnnotations === []) { return true; } $annotations = []; foreach (AnnotationHelper::getAnnotations($phpcsFile, $pointer) as $annotation) { $annotations[$annotation->getName()] = true; } foreach ($requiredAnnotations as $requiredAnnotation) { if (!array_key_exists('@' . $requiredAnnotation, $annotations)) { return false; } } return true; } /** * @param array<string> $requiredAttributes */ private function hasRequiredAttributes(File $phpcsFile, int $pointer, array $requiredAttributes): bool { if ($requiredAttributes === []) { return true; } $attributesClassNames = $this->getAttributeClassNamesForToken($phpcsFile, $pointer); foreach ($requiredAttributes as $requiredAttribute) { if (!array_key_exists(strtolower($requiredAttribute), $attributesClassNames)) { return false; } } return true; } /** * @return array<string, string> */ private function getAttributeClassNamesForToken(File $phpcsFile, int $pointer): array { $attributes = []; foreach (AttributeHelper::getAttributes($phpcsFile, $pointer) as $attribute) { $attributes[strtolower(ltrim($attribute->getFullyQualifiedName(), '\\'))] = $attribute->getFullyQualifiedName(); } return $attributes; } /** * @return int|string */ private function getVisibilityForToken(File $phpcsFile, int $pointer) { $tokens = $phpcsFile->getTokens(); $previousPointer = $pointer - 1; $endTokenCodes = [T_OPEN_CURLY_BRACKET, T_CLOSE_CURLY_BRACKET, T_SEMICOLON]; $tokenCodesToSearch = [...array_values(Tokens::$scopeModifiers), ...$endTokenCodes]; do { $previousPointer = TokenHelper::findPrevious($phpcsFile, $tokenCodesToSearch, $previousPointer - 1); if (in_array($tokens[$previousPointer]['code'], $endTokenCodes, true)) { // No visibility modifier found -> public return T_PUBLIC; } if (in_array($tokens[$previousPointer]['code'], [T_PROTECTED_SET, T_PRIVATE_SET], true)) { continue; } return $tokens[$previousPointer]['code']; } while (true); } 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); FixerHelper::addBefore($file, $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) { $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)) { return $tokens[$memberPointer]['scope_closer']; } if ($tokens[$memberPointer]['code'] === T_USE && array_key_exists('scope_closer', $tokens[$memberPointer])) { return $tokens[$memberPointer]['scope_closer']; } $endPointer = TokenHelper::findNext($phpcsFile, [T_SEMICOLON, T_OPEN_CURLY_BRACKET], $memberPointer + 1); return $tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET ? $tokens[$endPointer]['bracket_closer'] : $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++; FixerHelper::replace($phpcsFile, $whitespacePointer, ''); $whitespacePointer++; } return $linesToRemove; } /** * @return array<string, list<array{name: string|null, attributes: array<string>, annotations: array<string>}>> */ private function getNormalizedMethodGroups(): array { if ($this->normalizedMethodGroups === null) { $this->normalizedMethodGroups = []; $methodGroups = SniffSettingsHelper::normalizeAssociativeArray($this->methodGroups); foreach ($methodGroups as $group => $groupDefinition) { $group = strtolower((string) $group); $this->normalizedMethodGroups[$group] = []; $methodDefinitions = preg_split('~\\s*,\\s*~', (string) $groupDefinition, -1, PREG_SPLIT_NO_EMPTY); /** @var list<non-empty-string> $methodDefinitions */ foreach ($methodDefinitions as $methodDefinition) { $tokens = preg_split('~(?=[#@])~', $methodDefinition); /** @var non-empty-list<string> $tokens */ $method = array_shift($tokens); $methodRequirement = [ 'name' => $method !== '' ? $method : null, 'attributes' => [], 'annotations' => [], ]; foreach ($tokens as $token) { $key = $token[0] === '#' ? 'attributes' : 'annotations'; $methodRequirement[$key][] = substr($token, 1); } $this->normalizedMethodGroups[$group][] = $methodRequirement; } } } return $this->normalizedMethodGroups; } /** * @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, ]; $normalizedMethodGroups = $this->getNormalizedMethodGroups(); $normalizedGroupsWithShortcuts = []; $order = 1; foreach (SniffSettingsHelper::normalizeArray($this->groups) as $groupsString) { /** @var list<non-empty-string> $groups */ $groups = preg_split('~\\s*,\\s*~', strtolower($groupsString), -1, PREG_SPLIT_NO_EMPTY); foreach ($groups as $groupOrShortcut) { $groupOrShortcut = preg_replace('~\\s+~', ' ', $groupOrShortcut); if ( !in_array($groupOrShortcut, $supportedGroups, true) && !array_key_exists($groupOrShortcut, self::SHORTCUTS) && $groupOrShortcut !== self::GROUP_INVOKE_METHOD && !array_key_exists($groupOrShortcut, $normalizedMethodGroups) ) { throw new UnsupportedClassGroupException($groupOrShortcut); } $normalizedGroupsWithShortcuts[$groupOrShortcut] = $order; } $order++; } $normalizedGroups = []; foreach ($normalizedGroupsWithShortcuts as $groupOrShortcut => $groupOrder) { if ( in_array($groupOrShortcut, $supportedGroups, true) || $groupOrShortcut === self::GROUP_INVOKE_METHOD || array_key_exists($groupOrShortcut, $normalizedMethodGroups) ) { $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 === [] && $normalizedMethodGroups === []) { $normalizedGroups = array_flip($supportedGroups); } else { $missingGroups = array_diff( array_merge($supportedGroups, array_keys($normalizedMethodGroups)), array_keys($normalizedGroups), ); if ($missingGroups !== []) { throw new MissingClassGroupsException(array_values($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; } }
| ver. 1.6 |
Github
|
.
| PHP 8.2.30 | ??????????? ?????????: 0 |
proxy
|
phpinfo
|
???????????