├── .editorconfig ├── .typos.toml ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── SlevomatCodingStandard ├── Helpers │ ├── Annotation.php │ ├── AnnotationHelper.php │ ├── AnnotationTypeHelper.php │ ├── ArrayHelper.php │ ├── ArrayKeyValue.php │ ├── Attribute.php │ ├── AttributeHelper.php │ ├── CatchHelper.php │ ├── ClassHelper.php │ ├── Comment.php │ ├── CommentHelper.php │ ├── ConditionHelper.php │ ├── ConstantHelper.php │ ├── DocCommentHelper.php │ ├── EmptyFileException.php │ ├── FixerHelper.php │ ├── FunctionHelper.php │ ├── IdentificatorHelper.php │ ├── IndentationHelper.php │ ├── NamespaceHelper.php │ ├── ParameterHelper.php │ ├── ParsedDocComment.php │ ├── PhpDocParserHelper.php │ ├── PropertyHelper.php │ ├── ReferencedName.php │ ├── ReferencedNameHelper.php │ ├── ScopeHelper.php │ ├── SniffLocalCache.php │ ├── SniffSettingsHelper.php │ ├── StringHelper.php │ ├── SuppressHelper.php │ ├── TernaryOperatorHelper.php │ ├── TokenHelper.php │ ├── TokenPointerOutOfBoundsException.php │ ├── TypeHelper.php │ ├── TypeHint.php │ ├── TypeHintHelper.php │ ├── UseStatement.php │ ├── UseStatementHelper.php │ ├── VariableHelper.php │ └── YodaHelper.php ├── Sniffs │ ├── Arrays │ │ ├── AlphabeticallySortedByKeysSniff.php │ │ ├── ArrayAccessSniff.php │ │ ├── DisallowImplicitArrayCreationSniff.php │ │ ├── DisallowPartiallyKeyedSniff.php │ │ ├── MultiLineArrayEndBracketPlacementSniff.php │ │ ├── SingleLineArrayWhitespaceSniff.php │ │ └── TrailingArrayCommaSniff.php │ ├── Attributes │ │ ├── AttributeAndTargetSpacingSniff.php │ │ ├── AttributesOrderSniff.php │ │ ├── DisallowAttributesJoiningSniff.php │ │ ├── DisallowMultipleAttributesPerLineSniff.php │ │ └── RequireAttributeAfterDocCommentSniff.php │ ├── Classes │ │ ├── AbstractMethodSignature.php │ │ ├── AbstractPropertyConstantAndEnumCaseSpacing.php │ │ ├── BackedEnumTypeSpacingSniff.php │ │ ├── ClassConstantVisibilitySniff.php │ │ ├── ClassLengthSniff.php │ │ ├── ClassMemberSpacingSniff.php │ │ ├── ClassStructureSniff.php │ │ ├── ConstantSpacingSniff.php │ │ ├── DisallowConstructorPropertyPromotionSniff.php │ │ ├── DisallowLateStaticBindingForConstantsSniff.php │ │ ├── DisallowMultiConstantDefinitionSniff.php │ │ ├── DisallowMultiPropertyDefinitionSniff.php │ │ ├── DisallowStringExpressionPropertyFetchSniff.php │ │ ├── EmptyLinesAroundClassBracesSniff.php │ │ ├── EnumCaseSpacingSniff.php │ │ ├── ForbiddenPublicPropertySniff.php │ │ ├── MethodSpacingSniff.php │ │ ├── MissingClassGroupsException.php │ │ ├── ModernClassNameReferenceSniff.php │ │ ├── ParentCallSpacingSniff.php │ │ ├── PropertyDeclarationSniff.php │ │ ├── PropertySpacingSniff.php │ │ ├── RequireAbstractOrFinalSniff.php │ │ ├── RequireConstructorPropertyPromotionSniff.php │ │ ├── RequireMultiLineMethodSignatureSniff.php │ │ ├── RequireSelfReferenceSniff.php │ │ ├── RequireSingleLineMethodSignatureSniff.php │ │ ├── SuperfluousAbstractClassNamingSniff.php │ │ ├── SuperfluousErrorNamingSniff.php │ │ ├── SuperfluousExceptionNamingSniff.php │ │ ├── SuperfluousInterfaceNamingSniff.php │ │ ├── SuperfluousTraitNamingSniff.php │ │ ├── TraitUseDeclarationSniff.php │ │ ├── TraitUseSpacingSniff.php │ │ ├── UnsupportedClassGroupException.php │ │ └── UselessLateStaticBindingSniff.php │ ├── Commenting │ │ ├── AbstractRequireOneLineDocComment.php │ │ ├── AnnotationNameSniff.php │ │ ├── DeprecatedAnnotationDeclarationSniff.php │ │ ├── DisallowCommentAfterCodeSniff.php │ │ ├── DisallowOneLinePropertyDocCommentSniff.php │ │ ├── DocCommentSpacingSniff.php │ │ ├── EmptyCommentSniff.php │ │ ├── ForbiddenAnnotationsSniff.php │ │ ├── ForbiddenCommentsSniff.php │ │ ├── InlineDocCommentDeclarationSniff.php │ │ ├── RequireOneLineDocCommentSniff.php │ │ ├── RequireOneLinePropertyDocCommentSniff.php │ │ ├── UselessFunctionDocCommentSniff.php │ │ └── UselessInheritDocCommentSniff.php │ ├── Complexity │ │ └── CognitiveSniff.php │ ├── ControlStructures │ │ ├── AbstractControlStructureSpacing.php │ │ ├── AbstractLineCondition.php │ │ ├── AssignmentInConditionSniff.php │ │ ├── BlockControlStructureSpacingSniff.php │ │ ├── DisallowContinueWithoutIntegerOperandInSwitchSniff.php │ │ ├── DisallowEmptySniff.php │ │ ├── DisallowNullSafeObjectOperatorSniff.php │ │ ├── DisallowShortTernaryOperatorSniff.php │ │ ├── DisallowTrailingMultiLineTernaryOperatorSniff.php │ │ ├── DisallowYodaComparisonSniff.php │ │ ├── EarlyExitSniff.php │ │ ├── JumpStatementsSpacingSniff.php │ │ ├── LanguageConstructWithParenthesesSniff.php │ │ ├── NewWithParenthesesSniff.php │ │ ├── NewWithoutParenthesesSniff.php │ │ ├── RequireMultiLineConditionSniff.php │ │ ├── RequireMultiLineTernaryOperatorSniff.php │ │ ├── RequireNullCoalesceEqualOperatorSniff.php │ │ ├── RequireNullCoalesceOperatorSniff.php │ │ ├── RequireNullSafeObjectOperatorSniff.php │ │ ├── RequireShortTernaryOperatorSniff.php │ │ ├── RequireSingleLineConditionSniff.php │ │ ├── RequireTernaryOperatorSniff.php │ │ ├── RequireYodaComparisonSniff.php │ │ ├── UnsupportedKeywordException.php │ │ ├── UselessIfConditionWithReturnSniff.php │ │ └── UselessTernaryOperatorSniff.php │ ├── Exceptions │ │ ├── DeadCatchSniff.php │ │ ├── DisallowNonCapturingCatchSniff.php │ │ ├── ReferenceThrowableOnlySniff.php │ │ └── RequireNonCapturingCatchSniff.php │ ├── Files │ │ ├── FileLengthSniff.php │ │ ├── FilepathNamespaceExtractor.php │ │ ├── LineLengthSniff.php │ │ └── TypeNameMatchesFileNameSniff.php │ ├── Functions │ │ ├── AbstractLineCall.php │ │ ├── ArrowFunctionDeclarationSniff.php │ │ ├── DisallowArrowFunctionSniff.php │ │ ├── DisallowEmptyFunctionSniff.php │ │ ├── DisallowNamedArgumentsSniff.php │ │ ├── DisallowTrailingCommaInCallSniff.php │ │ ├── DisallowTrailingCommaInClosureUseSniff.php │ │ ├── DisallowTrailingCommaInDeclarationSniff.php │ │ ├── FunctionLengthSniff.php │ │ ├── NamedArgumentSpacingSniff.php │ │ ├── RequireArrowFunctionSniff.php │ │ ├── RequireMultiLineCallSniff.php │ │ ├── RequireSingleLineCallSniff.php │ │ ├── RequireTrailingCommaInCallSniff.php │ │ ├── RequireTrailingCommaInClosureUseSniff.php │ │ ├── RequireTrailingCommaInDeclarationSniff.php │ │ ├── StaticClosureSniff.php │ │ ├── StrictCallSniff.php │ │ ├── UnusedInheritedVariablePassedToClosureSniff.php │ │ ├── UnusedParameterSniff.php │ │ └── UselessParameterDefaultValueSniff.php │ ├── Namespaces │ │ ├── AbstractFullyQualifiedGlobalReference.php │ │ ├── AlphabeticallySortedUsesSniff.php │ │ ├── DisallowGroupUseSniff.php │ │ ├── FullyQualifiedClassNameInAnnotationSniff.php │ │ ├── FullyQualifiedExceptionsSniff.php │ │ ├── FullyQualifiedGlobalConstantsSniff.php │ │ ├── FullyQualifiedGlobalFunctionsSniff.php │ │ ├── MultipleUsesPerLineSniff.php │ │ ├── NamespaceDeclarationSniff.php │ │ ├── NamespaceSpacingSniff.php │ │ ├── ReferenceUsedNamesOnlySniff.php │ │ ├── RequireOneNamespaceInFileSniff.php │ │ ├── UnusedUsesSniff.php │ │ ├── UseDoesNotStartWithBackslashSniff.php │ │ ├── UseFromSameNamespaceSniff.php │ │ ├── UseOnlyWhitelistedNamespacesSniff.php │ │ ├── UseSpacingSniff.php │ │ └── UselessAliasSniff.php │ ├── Numbers │ │ ├── DisallowNumericLiteralSeparatorSniff.php │ │ └── RequireNumericLiteralSeparatorSniff.php │ ├── Operators │ │ ├── DisallowEqualOperatorsSniff.php │ │ ├── DisallowIncrementAndDecrementOperatorsSniff.php │ │ ├── NegationOperatorSpacingSniff.php │ │ ├── RequireCombinedAssignmentOperatorSniff.php │ │ ├── RequireOnlyStandaloneIncrementAndDecrementOperatorsSniff.php │ │ └── SpreadOperatorSpacingSniff.php │ ├── PHP │ │ ├── DisallowDirectMagicInvokeCallSniff.php │ │ ├── DisallowReferenceSniff.php │ │ ├── ForbiddenClassesSniff.php │ │ ├── OptimizedFunctionsWithoutUnpackingSniff.php │ │ ├── ReferenceSpacingSniff.php │ │ ├── RequireExplicitAssertionSniff.php │ │ ├── RequireNowdocSniff.php │ │ ├── ShortListSniff.php │ │ ├── TypeCastSniff.php │ │ ├── UselessParenthesesSniff.php │ │ └── UselessSemicolonSniff.php │ ├── Strings │ │ └── DisallowVariableParsingSniff.php │ ├── TestCase.php │ ├── TypeHints │ │ ├── ClassConstantTypeHintSniff.php │ │ ├── DNFTypeHintFormatSniff.php │ │ ├── DeclareStrictTypesSniff.php │ │ ├── DisallowArrayTypeHintSyntaxSniff.php │ │ ├── DisallowMixedTypeHintSniff.php │ │ ├── LongTypeHintsSniff.php │ │ ├── NullTypeHintOnLastPositionSniff.php │ │ ├── NullableTypeForNullDefaultValueSniff.php │ │ ├── ParameterTypeHintSniff.php │ │ ├── ParameterTypeHintSpacingSniff.php │ │ ├── PropertyTypeHintSniff.php │ │ ├── ReturnTypeHintSniff.php │ │ ├── ReturnTypeHintSpacingSniff.php │ │ ├── UnionTypeHintFormatSniff.php │ │ └── UselessConstantTypeHintSniff.php │ ├── Variables │ │ ├── DisallowSuperGlobalVariableSniff.php │ │ ├── DisallowVariableVariableSniff.php │ │ ├── DuplicateAssignmentToVariableSniff.php │ │ ├── UnusedVariableSniff.php │ │ └── UselessVariableSniff.php │ └── Whitespaces │ │ └── DuplicateSpacesSniff.php └── ruleset.xml ├── autoload-bootstrap.php ├── composer.json └── doc ├── arrays.md ├── attributes.md ├── classes.md ├── commenting.md ├── complexity.md ├── control-structures.md ├── exceptions.md ├── files.md ├── functions.md ├── namespaces.md ├── numbers.md ├── operators.md ├── php.md ├── strings.md ├── type-hints.md ├── variables.md └── whitespaces.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = tab 8 | insert_final_newline = true 9 | tab_width = 4 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = [ 3 | ".git/", 4 | ] 5 | ignore-hidden = false 6 | 7 | [default] 8 | extend-ignore-re = [ 9 | "const BA =", 10 | ] 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Slevomat.cz, s.r.o. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/Annotation.php: -------------------------------------------------------------------------------- 1 | node = $node; 25 | $this->startPointer = $startPointer; 26 | $this->endPointer = $endPointer; 27 | } 28 | 29 | public function getNode(): PhpDocTagNode 30 | { 31 | return $this->node; 32 | } 33 | 34 | public function getName(): string 35 | { 36 | return $this->node->name; 37 | } 38 | 39 | /** 40 | * @return T 41 | */ 42 | public function getValue(): PhpDocTagValueNode 43 | { 44 | /** @phpstan-ignore-next-line */ 45 | return $this->node->value; 46 | } 47 | 48 | public function getStartPointer(): int 49 | { 50 | return $this->startPointer; 51 | } 52 | 53 | public function getEndPointer(): int 54 | { 55 | return $this->endPointer; 56 | } 57 | 58 | public function isInvalid(): bool 59 | { 60 | return $this->node->value instanceof InvalidTagValueNode; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/Attribute.php: -------------------------------------------------------------------------------- 1 | attributePointer = $attributePointer; 24 | $this->name = $name; 25 | $this->startPointer = $startPointer; 26 | $this->endPointer = $endPointer; 27 | $this->content = $content; 28 | } 29 | 30 | public function getAttributePointer(): int 31 | { 32 | return $this->attributePointer; 33 | } 34 | 35 | public function getName(): string 36 | { 37 | return $this->name; 38 | } 39 | 40 | public function getStartPointer(): int 41 | { 42 | return $this->startPointer; 43 | } 44 | 45 | public function getEndPointer(): int 46 | { 47 | return $this->endPointer; 48 | } 49 | 50 | public function getContent(): ?string 51 | { 52 | return $this->content; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/CatchHelper.php: -------------------------------------------------------------------------------- 1 | getTokens(); 20 | 21 | $endPointer = $tokens[$catchPointer]['scope_closer']; 22 | 23 | do { 24 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $endPointer + 1); 25 | 26 | if ($nextPointer === null || !in_array($tokens[$nextPointer]['code'], [T_CATCH, T_FINALLY], true)) { 27 | break; 28 | } 29 | 30 | $endPointer = $tokens[$nextPointer]['scope_closer']; 31 | 32 | } while (true); 33 | 34 | return $endPointer; 35 | } 36 | 37 | /** 38 | * @param array|int|string> $catchToken 39 | * @return list 40 | */ 41 | public static function findCaughtTypesInCatch(File $phpcsFile, array $catchToken): array 42 | { 43 | /** @var int $catchParenthesisOpenerPointer */ 44 | $catchParenthesisOpenerPointer = $catchToken['parenthesis_opener']; 45 | /** @var int $catchParenthesisCloserPointer */ 46 | $catchParenthesisCloserPointer = $catchToken['parenthesis_closer']; 47 | 48 | $nameEndPointer = $catchParenthesisOpenerPointer; 49 | $tokens = $phpcsFile->getTokens(); 50 | $caughtTypes = []; 51 | do { 52 | $nameStartPointer = TokenHelper::findNext( 53 | $phpcsFile, 54 | [T_BITWISE_OR, ...TokenHelper::NAME_TOKEN_CODES], 55 | $nameEndPointer + 1, 56 | $catchParenthesisCloserPointer, 57 | ); 58 | if ($nameStartPointer === null) { 59 | break; 60 | } 61 | 62 | if ($tokens[$nameStartPointer]['code'] === T_BITWISE_OR) { 63 | /** @var int $nameStartPointer */ 64 | $nameStartPointer = TokenHelper::findNextEffective($phpcsFile, $nameStartPointer + 1, $catchParenthesisCloserPointer); 65 | } 66 | 67 | $pointerAfterNameEndPointer = TokenHelper::findNextExcluding($phpcsFile, TokenHelper::NAME_TOKEN_CODES, $nameStartPointer + 1); 68 | $nameEndPointer = $pointerAfterNameEndPointer === null ? $nameStartPointer : $pointerAfterNameEndPointer - 1; 69 | 70 | $caughtTypes[] = NamespaceHelper::resolveClassName( 71 | $phpcsFile, 72 | TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), 73 | $catchParenthesisOpenerPointer, 74 | ); 75 | } while (true); 76 | 77 | return $caughtTypes; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/Comment.php: -------------------------------------------------------------------------------- 1 | pointer = $pointer; 18 | $this->content = $content; 19 | } 20 | 21 | public function getPointer(): int 22 | { 23 | return $this->pointer; 24 | } 25 | 26 | public function getContent(): string 27 | { 28 | return $this->content; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/CommentHelper.php: -------------------------------------------------------------------------------- 1 | getTokens()[$commentPointer]['content']); 22 | } 23 | 24 | public static function getCommentEndPointer(File $phpcsFile, int $commentStartPointer): ?int 25 | { 26 | $tokens = $phpcsFile->getTokens(); 27 | 28 | if (array_key_exists('comment_closer', $tokens[$commentStartPointer])) { 29 | return $tokens[$commentStartPointer]['comment_closer']; 30 | } 31 | 32 | if (self::isLineComment($phpcsFile, $commentStartPointer)) { 33 | return $commentStartPointer; 34 | } 35 | 36 | if (strpos($tokens[$commentStartPointer]['content'], '/*') !== 0) { 37 | // Part of block comment 38 | return null; 39 | } 40 | 41 | $commentEndPointer = $commentStartPointer; 42 | 43 | for ($i = $commentStartPointer + 1; $i < $phpcsFile->numTokens; $i++) { 44 | if ($tokens[$i]['code'] === T_COMMENT) { 45 | $commentEndPointer = $i; 46 | continue; 47 | } 48 | 49 | if (in_array($tokens[$i]['code'], Tokens::$phpcsCommentTokens, true)) { 50 | $commentEndPointer = $i; 51 | continue; 52 | } 53 | 54 | break; 55 | } 56 | 57 | return $commentEndPointer; 58 | } 59 | 60 | public static function getMultilineCommentStartPointer(File $phpcsFile, int $commentEndPointer): int 61 | { 62 | $tokens = $phpcsFile->getTokens(); 63 | 64 | $commentStartPointer = $commentEndPointer; 65 | do { 66 | $commentBefore = TokenHelper::findPrevious($phpcsFile, TokenHelper::INLINE_COMMENT_TOKEN_CODES, $commentStartPointer - 1); 67 | if ($commentBefore === null) { 68 | break; 69 | } 70 | if ($tokens[$commentBefore]['line'] + 1 !== $tokens[$commentStartPointer]['line']) { 71 | break; 72 | } 73 | 74 | /** @var int $commentStartPointer */ 75 | $commentStartPointer = $commentBefore; 76 | } while (true); 77 | 78 | return $commentStartPointer; 79 | } 80 | 81 | public static function getMultilineCommentEndPointer(File $phpcsFile, int $commentStartPointer): int 82 | { 83 | $tokens = $phpcsFile->getTokens(); 84 | 85 | $commentEndPointer = $commentStartPointer; 86 | do { 87 | $commentAfter = TokenHelper::findNext($phpcsFile, TokenHelper::INLINE_COMMENT_TOKEN_CODES, $commentEndPointer + 1); 88 | if ($commentAfter === null) { 89 | break; 90 | } 91 | if ($tokens[$commentAfter]['line'] - 1 !== $tokens[$commentEndPointer]['line']) { 92 | break; 93 | } 94 | 95 | /** @var int $commentEndPointer */ 96 | $commentEndPointer = $commentAfter; 97 | } while (true); 98 | 99 | return $commentEndPointer; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/ConstantHelper.php: -------------------------------------------------------------------------------- 1 | getTokens(); 26 | return $tokens[TokenHelper::findNext($phpcsFile, T_STRING, $constantPointer + 1)]['content']; 27 | } 28 | 29 | public static function getFullyQualifiedName(File $phpcsFile, int $constantPointer): string 30 | { 31 | $name = self::getName($phpcsFile, $constantPointer); 32 | $namespace = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $constantPointer); 33 | 34 | return $namespace !== null 35 | ? sprintf('%s%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, NamespaceHelper::NAMESPACE_SEPARATOR, $name) 36 | : $name; 37 | } 38 | 39 | /** 40 | * @return list 41 | */ 42 | public static function getAllNames(File $phpcsFile): array 43 | { 44 | $previousConstantPointer = 0; 45 | 46 | return array_map( 47 | static fn (int $constantPointer): string => self::getName($phpcsFile, $constantPointer), 48 | array_values(array_filter( 49 | iterator_to_array(self::getAllConstantPointers($phpcsFile, $previousConstantPointer)), 50 | static function (int $constantPointer) use ($phpcsFile): bool { 51 | foreach (array_reverse($phpcsFile->getTokens()[$constantPointer]['conditions']) as $conditionTokenCode) { 52 | return $conditionTokenCode === T_NAMESPACE; 53 | } 54 | 55 | return true; 56 | }, 57 | )), 58 | ); 59 | } 60 | 61 | /** 62 | * @return Generator 63 | */ 64 | private static function getAllConstantPointers(File $phpcsFile, int &$previousConstantPointer): Generator 65 | { 66 | do { 67 | $nextConstantPointer = TokenHelper::findNext($phpcsFile, T_CONST, $previousConstantPointer + 1); 68 | if ($nextConstantPointer === null) { 69 | break; 70 | } 71 | 72 | $previousConstantPointer = $nextConstantPointer; 73 | 74 | yield $nextConstantPointer; 75 | } while (true); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/EmptyFileException.php: -------------------------------------------------------------------------------- 1 | filename = $filename; 25 | } 26 | 27 | public function getFilename(): string 28 | { 29 | return $this->filename; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/FixerHelper.php: -------------------------------------------------------------------------------- 1 | fixer->replaceToken($startPointer, $content); 19 | } 20 | 21 | public static function removeBetween(File $phpcsFile, int $startPointer, int $endPointer): void 22 | { 23 | self::removeBetweenIncluding($phpcsFile, $startPointer + 1, $endPointer - 1); 24 | } 25 | 26 | public static function removeBetweenIncluding(File $phpcsFile, int $startPointer, int $endPointer): void 27 | { 28 | for ($i = $startPointer; $i <= $endPointer; $i++) { 29 | $phpcsFile->fixer->replaceToken($i, ''); 30 | } 31 | } 32 | 33 | public static function removeWhitespaceBefore(File $phpcsFile, int $pointer): void 34 | { 35 | for ($i = $pointer - 1; $i > 0; $i--) { 36 | if (preg_match('~^\\s+$~', $phpcsFile->fixer->getTokenContent($i)) === 0) { 37 | break; 38 | } 39 | 40 | $phpcsFile->fixer->replaceToken($i, ''); 41 | } 42 | } 43 | 44 | public static function removeWhitespaceAfter(File $phpcsFile, int $pointer): void 45 | { 46 | for ($i = $pointer + 1; $i < count($phpcsFile->getTokens()); $i++) { 47 | if (preg_match('~^\\s+$~', $phpcsFile->fixer->getTokenContent($i)) === 0) { 48 | break; 49 | } 50 | 51 | $phpcsFile->fixer->replaceToken($i, ''); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/ParameterHelper.php: -------------------------------------------------------------------------------- 1 | getTokens(); 20 | 21 | if (!array_key_exists('nested_parenthesis', $tokens[$variablePointer])) { 22 | return false; 23 | } 24 | 25 | $parenthesisOpenerPointer = array_reverse(array_keys($tokens[$variablePointer]['nested_parenthesis']))[0]; 26 | if (!array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) { 27 | return false; 28 | } 29 | 30 | $parenthesisOwnerPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_owner']; 31 | return in_array($tokens[$parenthesisOwnerPointer]['code'], TokenHelper::FUNCTION_TOKEN_CODES, true); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/ParsedDocComment.php: -------------------------------------------------------------------------------- 1 | openPointer = $openPointer; 32 | $this->closePointer = $closePointer; 33 | $this->node = $node; 34 | $this->tokens = $tokens; 35 | } 36 | 37 | public function getOpenPointer(): int 38 | { 39 | return $this->openPointer; 40 | } 41 | 42 | public function getClosePointer(): int 43 | { 44 | return $this->closePointer; 45 | } 46 | 47 | public function getNode(): PhpDocNode 48 | { 49 | return $this->node; 50 | } 51 | 52 | public function getTokens(): TokenIterator 53 | { 54 | return $this->tokens; 55 | } 56 | 57 | public function getNodeStartPointer(File $phpcsFile, Node $node): int 58 | { 59 | $tokens = $phpcsFile->getTokens(); 60 | 61 | $tagStartLine = $tokens[$this->openPointer]['line'] + $node->getAttribute('startLine') - 1; 62 | 63 | $searchPointer = $this->openPointer + 1; 64 | for ($i = $this->openPointer + 1; $i < $this->closePointer; $i++) { 65 | if ($tagStartLine === $tokens[$i]['line']) { 66 | $searchPointer = $i; 67 | break; 68 | } 69 | } 70 | 71 | return TokenHelper::findNext($phpcsFile, [...TokenHelper::ANNOTATION_TOKEN_CODES, T_DOC_COMMENT_STRING], $searchPointer); 72 | } 73 | 74 | public function getNodeEndPointer(File $phpcsFile, Node $node, int $nodeStartPointer): int 75 | { 76 | $tokens = $phpcsFile->getTokens(); 77 | 78 | $content = trim($this->tokens->getContentBetween( 79 | $node->getAttribute(Attribute::START_INDEX), 80 | $node->getAttribute(Attribute::END_INDEX) + 1, 81 | )); 82 | $length = strlen($content); 83 | 84 | $searchPointer = $nodeStartPointer; 85 | 86 | $content = ''; 87 | for ($i = $nodeStartPointer; $i < count($tokens); $i++) { 88 | $content .= $tokens[$i]['content']; 89 | 90 | if (strlen($content) >= $length) { 91 | $searchPointer = $i; 92 | break; 93 | } 94 | } 95 | 96 | return TokenHelper::findPrevious( 97 | $phpcsFile, 98 | [...TokenHelper::ANNOTATION_TOKEN_CODES, T_DOC_COMMENT_STRING], 99 | $searchPointer, 100 | ); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/PhpDocParserHelper.php: -------------------------------------------------------------------------------- 1 | traverse([$node]); 74 | 75 | return $cloneNode; 76 | } 77 | 78 | private static function getConfig(): ParserConfig 79 | { 80 | static $config; 81 | 82 | if ($config === null) { 83 | $config = new ParserConfig(['lines' => true, 'indexes' => true]); 84 | } 85 | 86 | return $config; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/ReferencedName.php: -------------------------------------------------------------------------------- 1 | nameAsReferencedInFile = $nameAsReferencedInFile; 26 | $this->startPointer = $startPointer; 27 | $this->endPointer = $endPointer; 28 | $this->type = $type; 29 | } 30 | 31 | public function getNameAsReferencedInFile(): string 32 | { 33 | return $this->nameAsReferencedInFile; 34 | } 35 | 36 | public function getStartPointer(): int 37 | { 38 | return $this->startPointer; 39 | } 40 | 41 | public function getType(): string 42 | { 43 | return $this->type; 44 | } 45 | 46 | public function getEndPointer(): int 47 | { 48 | return $this->endPointer; 49 | } 50 | 51 | public function isClass(): bool 52 | { 53 | return $this->type === self::TYPE_CLASS; 54 | } 55 | 56 | public function isConstant(): bool 57 | { 58 | return $this->type === self::TYPE_CONSTANT; 59 | } 60 | 61 | public function isFunction(): bool 62 | { 63 | return $this->type === self::TYPE_FUNCTION; 64 | } 65 | 66 | public function hasSameUseStatementType(UseStatement $useStatement): bool 67 | { 68 | return $this->getType() === $useStatement->getType(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/ScopeHelper.php: -------------------------------------------------------------------------------- 1 | getTokens(); 19 | 20 | $getScope = static function (int $pointer) use ($tokens): int { 21 | $scope = 0; 22 | 23 | foreach (array_reverse($tokens[$pointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { 24 | if (!in_array($conditionTokenCode, TokenHelper::FUNCTION_TOKEN_CODES, true)) { 25 | continue; 26 | } 27 | 28 | $scope = $tokens[$conditionPointer]['level'] + 1; 29 | break; 30 | } 31 | 32 | return $scope; 33 | }; 34 | 35 | return $getScope($firstPointer) === $getScope($secondPointer); 36 | } 37 | 38 | public static function getRootPointer(File $phpcsFile, int $pointer): int 39 | { 40 | $rootPointer = TokenHelper::findNext($phpcsFile, T_OPEN_TAG, 0); 41 | 42 | $rootPointers = array_reverse(self::getAllRootPointers($phpcsFile)); 43 | foreach ($rootPointers as $currentRootPointer) { 44 | if ($currentRootPointer < $pointer) { 45 | $rootPointer = $currentRootPointer; 46 | break; 47 | } 48 | } 49 | 50 | return $rootPointer; 51 | } 52 | 53 | /** 54 | * @return list 55 | */ 56 | public static function getAllRootPointers(File $phpcsFile): array 57 | { 58 | $lazyValue = static fn (): array => TokenHelper::findNextAll($phpcsFile, T_OPEN_TAG, 0); 59 | 60 | return SniffLocalCache::getAndSetIfNotCached($phpcsFile, 'openTagPointers', $lazyValue); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/SniffLocalCache.php: -------------------------------------------------------------------------------- 1 | > 19 | */ 20 | private static array $cache = []; 21 | 22 | /** 23 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint 24 | * @return mixed 25 | */ 26 | public static function getAndSetIfNotCached(File $phpcsFile, string $key, Closure $lazyValue) 27 | { 28 | $fixerLoops = $phpcsFile->fixer !== null ? $phpcsFile->fixer->loops : 0; 29 | $internalKey = sprintf('%s-%s', $phpcsFile->getFilename(), $key); 30 | 31 | self::setIfNotCached($fixerLoops, $internalKey, $lazyValue); 32 | 33 | return self::$cache[$fixerLoops][$internalKey] ?? null; 34 | } 35 | 36 | private static function setIfNotCached(int $fixerLoops, string $internalKey, Closure $lazyValue): void 37 | { 38 | if (array_key_exists($fixerLoops, self::$cache) && array_key_exists($internalKey, self::$cache[$fixerLoops])) { 39 | return; 40 | } 41 | 42 | self::$cache[$fixerLoops][$internalKey] = $lazyValue(); 43 | 44 | if ($fixerLoops > 0) { 45 | unset(self::$cache[$fixerLoops - 1]); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/SniffSettingsHelper.php: -------------------------------------------------------------------------------- 1 | $settings 38 | * @return list 39 | */ 40 | public static function normalizeArray(array $settings): array 41 | { 42 | $settings = array_map(static fn (string $value): string => trim($value), $settings); 43 | $settings = array_filter($settings, static fn (string $value): bool => $value !== ''); 44 | return array_values($settings); 45 | } 46 | 47 | /** 48 | * @param array $settings 49 | * @return array 50 | */ 51 | public static function normalizeAssociativeArray(array $settings): array 52 | { 53 | $normalizedSettings = []; 54 | foreach ($settings as $key => $value) { 55 | if (is_string($key)) { 56 | $key = trim($key); 57 | } 58 | if (is_string($value)) { 59 | $value = trim($value); 60 | } 61 | if ($key === '' || $value === '') { 62 | continue; 63 | } 64 | $normalizedSettings[$key] = $value; 65 | } 66 | 67 | return $normalizedSettings; 68 | } 69 | 70 | public static function isValidRegularExpression(string $expression): bool 71 | { 72 | return preg_match('~^(?:\(.*\)|\{.*\}|\[.*\])[a-z]*\z~i', $expression) !== 0 73 | || preg_match('~^([^a-z\s\\\\]).*\\1[a-z]*\z~i', $expression) !== 0; 74 | } 75 | 76 | public static function isEnabledByPhpVersion(?bool $value, int $phpVersionLimit): bool 77 | { 78 | if ($value !== null) { 79 | return $value; 80 | } 81 | 82 | $phpVersion = Config::getConfigData('php_version') !== null ? (int) Config::getConfigData('php_version') : PHP_VERSION_ID; 83 | return $phpVersion >= $phpVersionLimit; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/StringHelper.php: -------------------------------------------------------------------------------- 1 | > $annotations */ 27 | $annotations = AnnotationHelper::getAnnotations($phpcsFile, $pointer, self::ANNOTATION); 28 | 29 | return array_reduce( 30 | $annotations, 31 | static function (bool $carry, Annotation $annotation) use ($suppressName): bool { 32 | $annotationSuppressName = explode(' ', $annotation->getValue()->value)[0]; 33 | 34 | if ( 35 | $suppressName === $annotationSuppressName 36 | || strpos($suppressName, sprintf('%s.', $annotationSuppressName)) === 0 37 | ) { 38 | $carry = true; 39 | } 40 | 41 | return $carry; 42 | }, 43 | false, 44 | ); 45 | } 46 | 47 | public static function removeSuppressAnnotation(File $phpcsFile, int $pointer, string $suppressName): void 48 | { 49 | $suppressAnnotation = null; 50 | /** @var Annotation $annotation */ 51 | foreach (AnnotationHelper::getAnnotations($phpcsFile, $pointer, self::ANNOTATION) as $annotation) { 52 | if ($annotation->getValue()->value === $suppressName) { 53 | $suppressAnnotation = $annotation; 54 | break; 55 | } 56 | } 57 | 58 | assert($suppressAnnotation !== null); 59 | 60 | $tokens = $phpcsFile->getTokens(); 61 | 62 | /** @var int $pointerBefore */ 63 | $pointerBefore = TokenHelper::findPrevious( 64 | $phpcsFile, 65 | [T_DOC_COMMENT_OPEN_TAG, T_DOC_COMMENT_STAR], 66 | $suppressAnnotation->getStartPointer() - 1, 67 | ); 68 | 69 | $changeStart = $tokens[$pointerBefore]['code'] === T_DOC_COMMENT_STAR ? $pointerBefore : $suppressAnnotation->getStartPointer(); 70 | 71 | /** @var int $changeEnd */ 72 | $changeEnd = TokenHelper::findNext( 73 | $phpcsFile, 74 | [T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT_STAR], 75 | $suppressAnnotation->getEndPointer() + 1, 76 | ) - 1; 77 | 78 | $phpcsFile->fixer->beginChangeset(); 79 | FixerHelper::removeBetweenIncluding($phpcsFile, $changeStart, $changeEnd); 80 | $phpcsFile->fixer->endChangeset(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/TokenPointerOutOfBoundsException.php: -------------------------------------------------------------------------------- 1 | pointer = $pointer; 32 | $this->lastTokenPointer = $lastTokenPointer; 33 | } 34 | 35 | public function getPointer(): int 36 | { 37 | return $this->pointer; 38 | } 39 | 40 | public function getLastTokenPointer(): int 41 | { 42 | return $this->lastTokenPointer; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/TypeHelper.php: -------------------------------------------------------------------------------- 1 | typeHint = $typeHint; 25 | $this->nullable = $nullable; 26 | $this->startPointer = $startPointer; 27 | $this->endPointer = $endPointer; 28 | } 29 | 30 | public function getTypeHint(): string 31 | { 32 | return $this->typeHint; 33 | } 34 | 35 | public function getTypeHintWithoutNullabilitySymbol(): string 36 | { 37 | return strpos($this->typeHint, '?') === 0 ? substr($this->typeHint, 1) : $this->typeHint; 38 | } 39 | 40 | public function isNullable(): bool 41 | { 42 | return $this->nullable; 43 | } 44 | 45 | public function getStartPointer(): int 46 | { 47 | return $this->startPointer; 48 | } 49 | 50 | public function getEndPointer(): int 51 | { 52 | return $this->endPointer; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Helpers/UseStatement.php: -------------------------------------------------------------------------------- 1 | nameAsReferencedInFile = $nameAsReferencedInFile; 39 | $this->normalizedNameAsReferencedInFile = self::normalizedNameAsReferencedInFile($type, $nameAsReferencedInFile); 40 | $this->fullyQualifiedTypeName = $fullyQualifiedClassName; 41 | $this->usePointer = $usePointer; 42 | $this->type = $type; 43 | $this->alias = $alias; 44 | } 45 | 46 | public function getNameAsReferencedInFile(): string 47 | { 48 | return $this->nameAsReferencedInFile; 49 | } 50 | 51 | public function getCanonicalNameAsReferencedInFile(): string 52 | { 53 | return $this->normalizedNameAsReferencedInFile; 54 | } 55 | 56 | public function getFullyQualifiedTypeName(): string 57 | { 58 | return $this->fullyQualifiedTypeName; 59 | } 60 | 61 | public function getPointer(): int 62 | { 63 | return $this->usePointer; 64 | } 65 | 66 | public function getType(): string 67 | { 68 | return $this->type; 69 | } 70 | 71 | public function getAlias(): ?string 72 | { 73 | return $this->alias; 74 | } 75 | 76 | public function isClass(): bool 77 | { 78 | return $this->type === self::TYPE_CLASS; 79 | } 80 | 81 | public function isConstant(): bool 82 | { 83 | return $this->type === self::TYPE_CONSTANT; 84 | } 85 | 86 | public function isFunction(): bool 87 | { 88 | return $this->type === self::TYPE_FUNCTION; 89 | } 90 | 91 | public function hasSameType(self $that): bool 92 | { 93 | return $this->type === $that->type; 94 | } 95 | 96 | public static function getUniqueId(string $type, string $name): string 97 | { 98 | $normalizedName = self::normalizedNameAsReferencedInFile($type, $name); 99 | 100 | if ($type === self::TYPE_CLASS) { 101 | return $normalizedName; 102 | } 103 | 104 | return sprintf('%s %s', $type, $normalizedName); 105 | } 106 | 107 | public static function normalizedNameAsReferencedInFile(string $type, string $name): string 108 | { 109 | if ($type === self::TYPE_CONSTANT) { 110 | return $name; 111 | } 112 | 113 | return strtolower($name); 114 | } 115 | 116 | public static function getTypeName(string $type): ?string 117 | { 118 | if ($type === self::TYPE_CONSTANT) { 119 | return 'const'; 120 | } 121 | 122 | if ($type === self::TYPE_FUNCTION) { 123 | return 'function'; 124 | } 125 | 126 | return null; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Arrays/AlphabeticallySortedByKeysSniff.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function register(): array 26 | { 27 | return TokenHelper::ARRAY_TOKEN_CODES; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $stackPointer 33 | */ 34 | public function process(File $phpcsFile, $stackPointer): void 35 | { 36 | if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) { 37 | return; 38 | } 39 | 40 | // "Parse" the array... get info for each key/value pair 41 | $keyValues = ArrayHelper::parse($phpcsFile, $stackPointer); 42 | 43 | if (ArrayHelper::isKeyedAll($keyValues) === false) { 44 | return; 45 | } 46 | 47 | if (ArrayHelper::isSortedByKey($keyValues)) { 48 | return; 49 | } 50 | 51 | $fix = $phpcsFile->addFixableError( 52 | 'Keyed multi-line arrays must be sorted alphabetically.', 53 | $stackPointer, 54 | self::CODE_INCORRECT_KEY_ORDER, 55 | ); 56 | if ($fix) { 57 | $this->fix($phpcsFile, $keyValues); 58 | } 59 | } 60 | 61 | /** 62 | * @param list $keyValues 63 | */ 64 | private function fix(File $phpcsFile, array $keyValues): void 65 | { 66 | $pointerStart = $keyValues[0]->getPointerStart(); 67 | $pointerEnd = $keyValues[count($keyValues) - 1]->getPointerEnd(); 68 | 69 | // determine indent to use 70 | $indent = ArrayHelper::getIndentation($keyValues); 71 | 72 | usort($keyValues, static fn ($a1, $a2) => strnatcasecmp((string) $a1->getKey(), (string) $a2->getKey())); 73 | 74 | $content = implode( 75 | '', 76 | array_map( 77 | static fn (ArrayKeyValue $keyValue) => $keyValue->getContent($phpcsFile, true, $indent) . $phpcsFile->eolChar, 78 | $keyValues, 79 | ), 80 | ); 81 | 82 | $phpcsFile->fixer->beginChangeset(); 83 | FixerHelper::change($phpcsFile, $pointerStart, $pointerEnd, $content); 84 | $phpcsFile->fixer->endChangeset(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Arrays/ArrayAccessSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return [T_OPEN_SQUARE_BRACKET]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $stackPointer 29 | */ 30 | public function process(File $phpcsFile, $stackPointer): void 31 | { 32 | $tokens = $phpcsFile->getTokens(); 33 | 34 | $previousToken = TokenHelper::findPreviousNonWhitespace($phpcsFile, $stackPointer - 1); 35 | 36 | if ( 37 | $previousToken === null 38 | || $previousToken === $stackPointer - 1) { 39 | return; 40 | } 41 | 42 | if ($tokens[$previousToken]['code'] === T_VARIABLE) { 43 | $this->addError( 44 | $phpcsFile, 45 | $stackPointer, 46 | 'There should be no space between array variable and array access operator.', 47 | self::CODE_NO_SPACE_BEFORE_BRACKETS, 48 | ); 49 | } 50 | 51 | if ($tokens[$previousToken]['code'] !== T_CLOSE_SQUARE_BRACKET) { 52 | return; 53 | } 54 | 55 | $this->addError( 56 | $phpcsFile, 57 | $stackPointer, 58 | 'There should be no space between array access operators.', 59 | self::CODE_NO_SPACE_BETWEEN_BRACKETS, 60 | ); 61 | } 62 | 63 | private function addError(File $phpcsFile, int $stackPointer, string $error, string $code): void 64 | { 65 | $fix = $phpcsFile->addFixableError($error, $stackPointer, $code); 66 | 67 | if (!$fix) { 68 | return; 69 | } 70 | 71 | $phpcsFile->fixer->beginChangeset(); 72 | $phpcsFile->fixer->replaceToken($stackPointer - 1, ''); 73 | $phpcsFile->fixer->endChangeset(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Arrays/DisallowPartiallyKeyedSniff.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function register(): array 19 | { 20 | return TokenHelper::ARRAY_TOKEN_CODES; 21 | } 22 | 23 | /** 24 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 25 | * @param int $stackPointer 26 | */ 27 | public function process(File $phpcsFile, $stackPointer): void 28 | { 29 | $keyValues = ArrayHelper::parse($phpcsFile, $stackPointer); 30 | 31 | if (!ArrayHelper::isKeyed($keyValues)) { 32 | return; 33 | } 34 | 35 | if (ArrayHelper::isKeyedAll($keyValues)) { 36 | return; 37 | } 38 | 39 | $phpcsFile->addError('Partially keyed array disallowed.', $stackPointer, self::CODE_DISALLOWED_PARTIALLY_KEYED); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Arrays/MultiLineArrayEndBracketPlacementSniff.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function register(): array 20 | { 21 | return TokenHelper::ARRAY_TOKEN_CODES; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $stackPointer 27 | */ 28 | public function process(File $phpcsFile, $stackPointer): void 29 | { 30 | $tokens = $phpcsFile->getTokens(); 31 | 32 | if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) { 33 | return; 34 | } 35 | 36 | [$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]); 37 | 38 | $nextEffective = TokenHelper::findNextEffective($phpcsFile, $arrayOpenerPointer + 1, $arrayCloserPointer); 39 | if ($nextEffective === null || in_array($tokens[$nextEffective]['code'], TokenHelper::ARRAY_TOKEN_CODES, true) === false) { 40 | return; 41 | } 42 | 43 | [$nextPointerOpener, $nextPointerCloser] = ArrayHelper::openClosePointers($tokens[$nextEffective]); 44 | 45 | $arraysStartAtSameLine = $tokens[$arrayOpenerPointer]['line'] === $tokens[$nextPointerOpener]['line']; 46 | $arraysEndAtSameLine = $tokens[$arrayCloserPointer]['line'] === $tokens[$nextPointerCloser]['line']; 47 | if (!$arraysStartAtSameLine || $arraysEndAtSameLine) { 48 | return; 49 | } 50 | 51 | $error = "Expected nested array to end at the same line as it's parent. Either put the nested array's end at the same line as the parent's end, or put the nested array start on it's own line."; 52 | $fix = $phpcsFile->addFixableError($error, $arrayOpenerPointer, self::CODE_ARRAY_END_WRONG_PLACEMENT); 53 | if (!$fix) { 54 | return; 55 | } 56 | 57 | $phpcsFile->fixer->addContent($arrayOpenerPointer, $phpcsFile->eolChar); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Arrays/TrailingArrayCommaSniff.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function register(): array 26 | { 27 | return TokenHelper::ARRAY_TOKEN_CODES; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $stackPointer 33 | */ 34 | public function process(File $phpcsFile, $stackPointer): void 35 | { 36 | $this->enableAfterHeredoc = SniffSettingsHelper::isEnabledByPhpVersion($this->enableAfterHeredoc, 70300); 37 | 38 | $tokens = $phpcsFile->getTokens(); 39 | 40 | [$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]); 41 | 42 | if ($tokens[$arrayOpenerPointer]['line'] === $tokens[$arrayCloserPointer]['line']) { 43 | return; 44 | } 45 | 46 | /** @var int $pointerPreviousToClose */ 47 | $pointerPreviousToClose = TokenHelper::findPreviousEffective($phpcsFile, $arrayCloserPointer - 1); 48 | $tokenPreviousToClose = $tokens[$pointerPreviousToClose]; 49 | 50 | if ( 51 | $pointerPreviousToClose === $arrayOpenerPointer 52 | || $tokenPreviousToClose['code'] === T_COMMA 53 | || $tokens[$arrayCloserPointer]['line'] === $tokenPreviousToClose['line'] 54 | ) { 55 | return; 56 | } 57 | 58 | if ( 59 | !$this->enableAfterHeredoc 60 | && in_array($tokenPreviousToClose['code'], [T_END_HEREDOC, T_END_NOWDOC], true) 61 | ) { 62 | return; 63 | } 64 | 65 | $fix = $phpcsFile->addFixableError( 66 | 'Multi-line arrays must have a trailing comma after the last element.', 67 | $pointerPreviousToClose, 68 | self::CODE_MISSING_TRAILING_COMMA, 69 | ); 70 | if (!$fix) { 71 | return; 72 | } 73 | 74 | $phpcsFile->fixer->beginChangeset(); 75 | $phpcsFile->fixer->addContent($pointerPreviousToClose, ','); 76 | $phpcsFile->fixer->endChangeset(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Attributes/DisallowAttributesJoiningSniff.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function register(): array 21 | { 22 | return [T_ATTRIBUTE]; 23 | } 24 | 25 | /** 26 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 27 | * @param int $attributeOpenerPointer 28 | */ 29 | public function process(File $phpcsFile, $attributeOpenerPointer): void 30 | { 31 | if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { 32 | return; 33 | } 34 | 35 | $attributes = AttributeHelper::getAttributes($phpcsFile, $attributeOpenerPointer); 36 | $attributeCount = count($attributes); 37 | 38 | if ($attributeCount === 1) { 39 | return; 40 | } 41 | 42 | $fix = $phpcsFile->addFixableError( 43 | sprintf('%d attributes are joined.', $attributeCount), 44 | $attributeOpenerPointer, 45 | self::CODE_DISALLOWED_ATTRIBUTES_JOINING, 46 | ); 47 | 48 | if (!$fix) { 49 | return; 50 | } 51 | 52 | $phpcsFile->fixer->beginChangeset(); 53 | 54 | for ($i = 1; $i < count($attributes); $i++) { 55 | $previousAttribute = $attributes[$i - 1]; 56 | $attribute = $attributes[$i]; 57 | 58 | $phpcsFile->fixer->addContent($previousAttribute->getEndPointer(), ']'); 59 | 60 | for ($j = $previousAttribute->getEndPointer() + 1; $j < $attribute->getStartPointer(); $j++) { 61 | if ($phpcsFile->fixer->getTokenContent($j) === ',') { 62 | $phpcsFile->fixer->replaceToken($j, ''); 63 | } 64 | } 65 | 66 | $phpcsFile->fixer->addContentBefore($attribute->getStartPointer(), '#['); 67 | } 68 | 69 | $phpcsFile->fixer->endChangeset(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Attributes/DisallowMultipleAttributesPerLineSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return [T_ATTRIBUTE]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $attributeOpenerPointer 29 | */ 30 | public function process(File $phpcsFile, $attributeOpenerPointer): void 31 | { 32 | if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { 33 | return; 34 | } 35 | 36 | $tokens = $phpcsFile->getTokens(); 37 | 38 | $attributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer']; 39 | 40 | $nextAttributeOpenerPointer = TokenHelper::findNext($phpcsFile, T_ATTRIBUTE, $attributeCloserPointer + 1); 41 | 42 | if ($nextAttributeOpenerPointer === null) { 43 | return; 44 | } 45 | 46 | if ($tokens[$attributeCloserPointer]['line'] !== $tokens[$nextAttributeOpenerPointer]['line']) { 47 | return; 48 | } 49 | 50 | $attributeTargetPointer = AttributeHelper::getAttributeTarget($phpcsFile, $attributeOpenerPointer); 51 | $nextAttributeTargetPointer = AttributeHelper::getAttributeTarget($phpcsFile, $nextAttributeOpenerPointer); 52 | 53 | if ($attributeTargetPointer !== $nextAttributeTargetPointer) { 54 | return; 55 | } 56 | 57 | $fix = $phpcsFile->addFixableError( 58 | 'Multiple attributes per line are disallowed.', 59 | $nextAttributeOpenerPointer, 60 | self::CODE_DISALLOWED_MULTIPLE_ATTRIBUTES_PER_LINE, 61 | ); 62 | 63 | if (!$fix) { 64 | return; 65 | } 66 | 67 | $nonWhitespacePointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $nextAttributeOpenerPointer - 1); 68 | $indentation = IndentationHelper::getIndentation( 69 | $phpcsFile, 70 | TokenHelper::findFirstNonWhitespaceOnLine($phpcsFile, $attributeOpenerPointer), 71 | ); 72 | 73 | $phpcsFile->fixer->beginChangeset(); 74 | 75 | FixerHelper::removeBetween($phpcsFile, $nonWhitespacePointerBefore, $nextAttributeOpenerPointer); 76 | 77 | $phpcsFile->fixer->addContentBefore($nextAttributeOpenerPointer, $phpcsFile->eolChar . $indentation); 78 | 79 | $phpcsFile->fixer->endChangeset(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Attributes/RequireAttributeAfterDocCommentSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [T_ATTRIBUTE]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $attributeOpenerPointer 31 | */ 32 | public function process(File $phpcsFile, $attributeOpenerPointer): void 33 | { 34 | if (!AttributeHelper::isValidAttribute($phpcsFile, $attributeOpenerPointer)) { 35 | return; 36 | } 37 | 38 | $tokens = $phpcsFile->getTokens(); 39 | 40 | $docCommentOpenerPointer = TokenHelper::findNextExcluding( 41 | $phpcsFile, 42 | T_WHITESPACE, 43 | $tokens[$attributeOpenerPointer]['attribute_closer'] + 1, 44 | ); 45 | 46 | if ($tokens[$docCommentOpenerPointer]['code'] !== T_DOC_COMMENT_OPEN_TAG) { 47 | return; 48 | } 49 | 50 | $docCommentStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $docCommentOpenerPointer); 51 | $docCommentEndPointer = TokenHelper::findLastTokenOnLine($phpcsFile, $tokens[$docCommentOpenerPointer]['comment_closer']); 52 | $docComment = TokenHelper::getContent($phpcsFile, $docCommentStartPointer, $docCommentEndPointer); 53 | 54 | $firstAttributeOpenerPointer = $attributeOpenerPointer; 55 | do { 56 | $nonWhitespacePointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $firstAttributeOpenerPointer - 1); 57 | 58 | if ($tokens[$nonWhitespacePointerBefore]['code'] !== T_ATTRIBUTE_END) { 59 | break; 60 | } 61 | 62 | $firstAttributeOpenerPointer = $tokens[$nonWhitespacePointerBefore]['attribute_opener']; 63 | } while (true); 64 | 65 | $attributeStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $firstAttributeOpenerPointer); 66 | 67 | $fix = $phpcsFile->addFixableError( 68 | 'Attribute should be placed after documentation comment.', 69 | $attributeOpenerPointer, 70 | self::CODE_ATTRIBUTE_BEFORE_DOC_COMMENT, 71 | ); 72 | 73 | if (!$fix) { 74 | return; 75 | } 76 | 77 | $phpcsFile->fixer->beginChangeset(); 78 | 79 | $phpcsFile->fixer->addContentBefore($attributeStartPointer, $docComment); 80 | 81 | FixerHelper::removeBetweenIncluding($phpcsFile, $docCommentStartPointer, $docCommentEndPointer); 82 | 83 | $phpcsFile->fixer->endChangeset(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/AbstractMethodSignature.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | public function register(): array 29 | { 30 | return [T_FUNCTION]; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | protected function getSignatureStartAndEndPointers(File $phpcsFile, int $methodPointer): array 37 | { 38 | $signatureStartPointer = TokenHelper::findFirstTokenOnLine($phpcsFile, $methodPointer); 39 | 40 | /** @var int $pointerAfterSignatureEnd */ 41 | $pointerAfterSignatureEnd = TokenHelper::findNext($phpcsFile, [T_OPEN_CURLY_BRACKET, T_SEMICOLON], $methodPointer + 1); 42 | if ($phpcsFile->getTokens()[$pointerAfterSignatureEnd]['code'] === T_SEMICOLON) { 43 | return [$signatureStartPointer, $pointerAfterSignatureEnd]; 44 | } 45 | 46 | /** @var int $signatureEndPointer */ 47 | $signatureEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointerAfterSignatureEnd - 1); 48 | 49 | return [$signatureStartPointer, $signatureEndPointer]; 50 | } 51 | 52 | protected function getSignature(File $phpcsFile, int $signatureStartPointer, int $signatureEndPointer): string 53 | { 54 | $signature = TokenHelper::getContent($phpcsFile, $signatureStartPointer, $signatureEndPointer); 55 | $signature = preg_replace(sprintf('~%s[ \t]*~', $phpcsFile->eolChar), ' ', $signature); 56 | assert(is_string($signature)); 57 | 58 | $signature = str_replace(['( ', ' )'], ['(', ')'], $signature); 59 | $signature = rtrim($signature); 60 | 61 | return $signature; 62 | } 63 | 64 | protected function getSignatureWithoutTabs(File $phpcsFile, string $signature): string 65 | { 66 | return IndentationHelper::convertTabsToSpaces($phpcsFile, $signature); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/ClassConstantVisibilitySniff.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | public function register(): array 32 | { 33 | return [ 34 | T_CONST, 35 | ]; 36 | } 37 | 38 | /** 39 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 40 | * @param int $constantPointer 41 | */ 42 | public function process(File $phpcsFile, $constantPointer): void 43 | { 44 | $tokens = $phpcsFile->getTokens(); 45 | 46 | if (count($tokens[$constantPointer]['conditions']) === 0) { 47 | return; 48 | } 49 | 50 | /** @var int $classPointer */ 51 | $classPointer = array_keys($tokens[$constantPointer]['conditions'])[count($tokens[$constantPointer]['conditions']) - 1]; 52 | if (!in_array($tokens[$classPointer]['code'], Tokens::$ooScopeTokens, true)) { 53 | return; 54 | } 55 | 56 | $visibilityPointer = TokenHelper::findPreviousEffective($phpcsFile, $constantPointer - 1); 57 | if ($tokens[$visibilityPointer]['code'] === T_FINAL) { 58 | $visibilityPointer = TokenHelper::findPreviousEffective($phpcsFile, $visibilityPointer - 1); 59 | } 60 | 61 | if (in_array($tokens[$visibilityPointer]['code'], [T_PUBLIC, T_PROTECTED, T_PRIVATE], true)) { 62 | return; 63 | } 64 | 65 | $equalSignPointer = TokenHelper::findNext($phpcsFile, T_EQUAL, $constantPointer + 1); 66 | $namePointer = TokenHelper::findPreviousEffective($phpcsFile, $equalSignPointer - 1); 67 | 68 | $message = sprintf( 69 | 'Constant %s::%s visibility missing.', 70 | ClassHelper::getFullyQualifiedName($phpcsFile, $classPointer), 71 | $tokens[$namePointer]['content'], 72 | ); 73 | 74 | if ($this->fixable) { 75 | $fix = $phpcsFile->addFixableError($message, $constantPointer, self::CODE_MISSING_CONSTANT_VISIBILITY); 76 | if ($fix) { 77 | $phpcsFile->fixer->beginChangeset(); 78 | $phpcsFile->fixer->addContentBefore($constantPointer, 'public '); 79 | $phpcsFile->fixer->endChangeset(); 80 | } 81 | } else { 82 | $phpcsFile->addError($message, $constantPointer, self::CODE_MISSING_CONSTANT_VISIBILITY); 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/ClassLengthSniff.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function register(): array 31 | { 32 | return array_values(Tokens::$ooScopeTokens); 33 | } 34 | 35 | /** 36 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 37 | * @param int $pointer 38 | */ 39 | public function process(File $phpcsFile, $pointer): void 40 | { 41 | $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); 42 | $flags = array_keys(array_filter([ 43 | FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, 44 | FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, 45 | ])); 46 | $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); 47 | 48 | $length = FunctionHelper::getLineCount($phpcsFile, $pointer, $flags); 49 | 50 | if ($length <= $this->maxLinesLength) { 51 | return; 52 | } 53 | 54 | $errorMessage = sprintf('Your class is too long. Currently using %d lines. Can be up to %d lines.', $length, $this->maxLinesLength); 55 | 56 | $phpcsFile->addError($errorMessage, $pointer, self::CODE_CLASS_TOO_LONG); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/ConstantSpacingSniff.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | public function register(): array 27 | { 28 | return [T_CONST]; 29 | } 30 | 31 | /** 32 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 33 | * @param int $constantPointer 34 | */ 35 | public function process(File $phpcsFile, $constantPointer): int 36 | { 37 | $tokens = $phpcsFile->getTokens(); 38 | 39 | if ($tokens[$constantPointer]['conditions'] === []) { 40 | return $constantPointer; 41 | } 42 | 43 | /** @var int $classPointer */ 44 | $classPointer = array_keys($tokens[$constantPointer]['conditions'])[count($tokens[$constantPointer]['conditions']) - 1]; 45 | if (!in_array($tokens[$classPointer]['code'], Tokens::$ooScopeTokens, true)) { 46 | return $constantPointer; 47 | } 48 | 49 | return parent::process($phpcsFile, $constantPointer); 50 | } 51 | 52 | protected function isNextMemberValid(File $phpcsFile, int $pointer): bool 53 | { 54 | $tokens = $phpcsFile->getTokens(); 55 | 56 | if ($tokens[$pointer]['code'] === T_CONST) { 57 | return true; 58 | } 59 | 60 | $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_ENUM_CASE, T_CONST, T_VARIABLE, T_USE], $pointer + 1); 61 | 62 | return $nextPointer !== null && $tokens[$nextPointer]['code'] === T_CONST; 63 | } 64 | 65 | protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool 66 | { 67 | if ($minExpectedLines === $maxExpectedLines) { 68 | $errorMessage = $minExpectedLines === 1 69 | ? 'Expected 1 blank line after constant, found %3$d.' 70 | : 'Expected %2$d blank lines after constant, found %3$d.'; 71 | } else { 72 | $errorMessage = 'Expected %1$d to %2$d blank lines after constant, found %3$d.'; 73 | } 74 | $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); 75 | 76 | return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_CONSTANT); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/DisallowConstructorPropertyPromotionSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return [T_FUNCTION]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $functionPointer 32 | */ 33 | public function process(File $phpcsFile, $functionPointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $namePointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); 38 | 39 | if (strtolower($tokens[$namePointer]['content']) !== '__construct') { 40 | return; 41 | } 42 | 43 | $modifierPointers = TokenHelper::findNextAll( 44 | $phpcsFile, 45 | [...array_values(Tokens::$scopeModifiers), T_READONLY], 46 | $tokens[$functionPointer]['parenthesis_opener'] + 1, 47 | $tokens[$functionPointer]['parenthesis_closer'], 48 | ); 49 | 50 | if ($modifierPointers === []) { 51 | return; 52 | } 53 | 54 | foreach ($modifierPointers as $modifierPointer) { 55 | $variablePointer = TokenHelper::findNext($phpcsFile, T_VARIABLE, $modifierPointer + 1); 56 | 57 | $phpcsFile->addError( 58 | sprintf( 59 | 'Constructor property promotion is disallowed, promotion of property %s found.', 60 | $tokens[$variablePointer]['content'], 61 | ), 62 | $variablePointer, 63 | self::CODE_DISALLOWED_CONSTRUCTOR_PROPERTY_PROMOTION, 64 | ); 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/DisallowLateStaticBindingForConstantsSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_STATIC, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $staticPointer 32 | */ 33 | public function process(File $phpcsFile, $staticPointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $doubleColonPointer = TokenHelper::findNextEffective($phpcsFile, $staticPointer + 1); 38 | if ($tokens[$doubleColonPointer]['code'] !== T_DOUBLE_COLON) { 39 | return; 40 | } 41 | 42 | $stringPointer = TokenHelper::findNextEffective($phpcsFile, $doubleColonPointer + 1); 43 | if ($tokens[$stringPointer]['code'] !== T_STRING) { 44 | return; 45 | } 46 | 47 | if (strtolower($tokens[$stringPointer]['content']) === 'class') { 48 | return; 49 | } 50 | 51 | $pointerAfterString = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); 52 | if ($tokens[$pointerAfterString]['code'] === T_OPEN_PARENTHESIS) { 53 | return; 54 | } 55 | 56 | $fix = $phpcsFile->addFixableError( 57 | 'Late static binding for constants is disallowed.', 58 | $staticPointer, 59 | self::CODE_DISALLOWED_LATE_STATIC_BINDING_FOR_CONSTANT, 60 | ); 61 | 62 | if (!$fix) { 63 | return; 64 | } 65 | 66 | $phpcsFile->fixer->beginChangeset(); 67 | $phpcsFile->fixer->replaceToken($staticPointer, 'self'); 68 | $phpcsFile->fixer->endChangeset(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/DisallowStringExpressionPropertyFetchSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [T_OBJECT_OPERATOR]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $objectOperatorPointer 31 | */ 32 | public function process(File $phpcsFile, $objectOperatorPointer): void 33 | { 34 | $tokens = $phpcsFile->getTokens(); 35 | 36 | $curlyBracketOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $objectOperatorPointer + 1); 37 | 38 | if ($tokens[$curlyBracketOpenerPointer]['code'] !== T_OPEN_CURLY_BRACKET) { 39 | return; 40 | } 41 | 42 | $curlyBracketCloserPointer = $tokens[$curlyBracketOpenerPointer]['bracket_closer']; 43 | 44 | if (TokenHelper::findNextExcluding( 45 | $phpcsFile, 46 | T_CONSTANT_ENCAPSED_STRING, 47 | $curlyBracketOpenerPointer + 1, 48 | $curlyBracketCloserPointer, 49 | ) !== null) { 50 | return; 51 | } 52 | 53 | $pointerAfterCurlyBracketCloser = TokenHelper::findNextEffective($phpcsFile, $curlyBracketCloserPointer + 1); 54 | 55 | if ($tokens[$pointerAfterCurlyBracketCloser]['code'] === T_OPEN_PARENTHESIS) { 56 | return; 57 | } 58 | 59 | if (preg_match( 60 | '~^(["\'])([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\1$~', 61 | $tokens[$curlyBracketOpenerPointer + 1]['content'], 62 | $matches, 63 | ) !== 1) { 64 | return; 65 | } 66 | 67 | $fix = $phpcsFile->addFixableError( 68 | 'String expression property fetch is disallowed, use identifier property fetch.', 69 | $curlyBracketOpenerPointer, 70 | self::CODE_DISALLOWED_STRING_EXPRESSION_PROPERTY_FETCH, 71 | ); 72 | 73 | if (!$fix) { 74 | return; 75 | } 76 | 77 | $phpcsFile->fixer->beginChangeset(); 78 | 79 | FixerHelper::change($phpcsFile, $curlyBracketOpenerPointer, $curlyBracketCloserPointer, $matches[2]); 80 | 81 | $phpcsFile->fixer->endChangeset(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/EnumCaseSpacingSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [T_ENUM_CASE]; 25 | } 26 | 27 | protected function isNextMemberValid(File $phpcsFile, int $pointer): bool 28 | { 29 | $tokens = $phpcsFile->getTokens(); 30 | 31 | if ($tokens[$pointer]['code'] === T_ENUM_CASE) { 32 | return true; 33 | } 34 | 35 | $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_CONST, T_VARIABLE, T_USE, T_ENUM_CASE], $pointer + 1); 36 | 37 | return $nextPointer !== null && $tokens[$nextPointer]['code'] === T_ENUM_CASE; 38 | } 39 | 40 | protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool 41 | { 42 | if ($minExpectedLines === $maxExpectedLines) { 43 | $errorMessage = $minExpectedLines === 1 44 | ? 'Expected 1 blank line after enum case, found %3$d.' 45 | : 'Expected %2$d blank lines after enum case, found %3$d.'; 46 | } else { 47 | $errorMessage = 'Expected %1$d to %2$d blank lines after enum case, found %3$d.'; 48 | } 49 | $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); 50 | 51 | return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_ENUM_CASE); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/MissingClassGroupsException.php: -------------------------------------------------------------------------------- 1 | $groups */ 13 | public function __construct(array $groups) 14 | { 15 | parent::__construct( 16 | sprintf( 17 | 'You need configure all class groups. These groups are missing from your configuration: %s.', 18 | implode(', ', $groups), 19 | ), 20 | ); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/PropertySpacingSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $pointer 32 | */ 33 | public function process(File $phpcsFile, $pointer): int 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $asPointer = TokenHelper::findPreviousEffective($phpcsFile, $pointer - 1); 38 | if ($tokens[$asPointer]['code'] === T_AS) { 39 | return $pointer; 40 | } 41 | 42 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $pointer + 1); 43 | if (in_array($tokens[$nextPointer]['code'], TokenHelper::PROPERTY_MODIFIERS_TOKEN_CODES, true)) { 44 | // We don't want to report the same property multiple times 45 | return $nextPointer; 46 | } 47 | 48 | $propertyPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_FUNCTION, T_CONST, T_USE], $pointer + 1); 49 | if ( 50 | $propertyPointer === null 51 | || $tokens[$propertyPointer]['code'] !== T_VARIABLE 52 | || !PropertyHelper::isProperty($phpcsFile, $propertyPointer) 53 | ) { 54 | return $propertyPointer ?? $pointer; 55 | } 56 | 57 | return parent::process($phpcsFile, $propertyPointer); 58 | } 59 | 60 | protected function isNextMemberValid(File $phpcsFile, int $pointer): bool 61 | { 62 | $nextPointer = TokenHelper::findNext($phpcsFile, [T_FUNCTION, T_VARIABLE], $pointer + 1); 63 | 64 | return $nextPointer !== null && $phpcsFile->getTokens()[$nextPointer]['code'] === T_VARIABLE; 65 | } 66 | 67 | protected function addError(File $phpcsFile, int $pointer, int $minExpectedLines, int $maxExpectedLines, int $found): bool 68 | { 69 | if ($minExpectedLines === $maxExpectedLines) { 70 | $errorMessage = $minExpectedLines === 1 71 | ? 'Expected 1 blank line after property, found %3$d.' 72 | : 'Expected %2$d blank lines after property, found %3$d.'; 73 | } else { 74 | $errorMessage = 'Expected %1$d to %2$d blank lines after property, found %3$d.'; 75 | } 76 | $error = sprintf($errorMessage, $minExpectedLines, $maxExpectedLines, $found); 77 | 78 | return $phpcsFile->addFixableError($error, $pointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_AFTER_PROPERTY); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/RequireAbstractOrFinalSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_CLASS, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $classPointer 32 | */ 33 | public function process(File $phpcsFile, $classPointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); 38 | 39 | if ($tokens[$previousPointer]['code'] === T_READONLY) { 40 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); 41 | } 42 | 43 | if (in_array($tokens[$previousPointer]['code'], [T_ABSTRACT, T_FINAL], true)) { 44 | return; 45 | } 46 | 47 | $fix = $phpcsFile->addFixableError( 48 | 'All classes should be declared using either the "abstract" or "final" keyword.', 49 | $classPointer, 50 | self::CODE_NO_ABSTRACT_OR_FINAL, 51 | ); 52 | 53 | if (!$fix) { 54 | return; 55 | } 56 | 57 | $phpcsFile->fixer->beginChangeset(); 58 | $phpcsFile->fixer->addContentBefore($classPointer, 'final '); 59 | $phpcsFile->fixer->endChangeset(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/SuperfluousAbstractClassNamingSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return [ 27 | T_CLASS, 28 | ]; 29 | } 30 | 31 | /** 32 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 33 | * @param int $classPointer 34 | */ 35 | public function process(File $phpcsFile, $classPointer): void 36 | { 37 | $className = ClassHelper::getName($phpcsFile, $classPointer); 38 | 39 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); 40 | if ($phpcsFile->getTokens()[$previousPointer]['code'] !== T_ABSTRACT) { 41 | return; 42 | } 43 | 44 | $this->checkPrefix($phpcsFile, $classPointer, $className); 45 | $this->checkSuffix($phpcsFile, $classPointer, $className); 46 | } 47 | 48 | private function checkPrefix(File $phpcsFile, int $classPointer, string $className): void 49 | { 50 | $prefix = substr($className, 0, 8); 51 | 52 | if (strtolower($prefix) !== 'abstract') { 53 | return; 54 | } 55 | 56 | $phpcsFile->addError(sprintf('Superfluous prefix "%s".', $prefix), $classPointer, self::CODE_SUPERFLUOUS_PREFIX); 57 | } 58 | 59 | private function checkSuffix(File $phpcsFile, int $classPointer, string $className): void 60 | { 61 | $suffix = substr($className, -8); 62 | 63 | if (strtolower($suffix) !== 'abstract') { 64 | return; 65 | } 66 | 67 | $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/SuperfluousErrorNamingSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_CLASS, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $classPointer 33 | */ 34 | public function process(File $phpcsFile, $classPointer): void 35 | { 36 | $className = ClassHelper::getName($phpcsFile, $classPointer); 37 | 38 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); 39 | if ($phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT) { 40 | return; 41 | } 42 | 43 | if (strtolower($className) === 'error') { 44 | return; 45 | } 46 | 47 | $suffix = substr($className, -5); 48 | if (strtolower($suffix) !== 'error') { 49 | return; 50 | } 51 | 52 | $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/SuperfluousExceptionNamingSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_CLASS, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $classPointer 33 | */ 34 | public function process(File $phpcsFile, $classPointer): void 35 | { 36 | $className = ClassHelper::getName($phpcsFile, $classPointer); 37 | 38 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $classPointer - 1); 39 | if ($phpcsFile->getTokens()[$previousPointer]['code'] === T_ABSTRACT) { 40 | return; 41 | } 42 | 43 | if (strtolower($className) === 'exception') { 44 | return; 45 | } 46 | 47 | $suffix = substr($className, -9); 48 | if (strtolower($suffix) !== 'exception') { 49 | return; 50 | } 51 | 52 | $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $classPointer, self::CODE_SUPERFLUOUS_SUFFIX); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/SuperfluousInterfaceNamingSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_INTERFACE, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $interfacePointer 32 | */ 33 | public function process(File $phpcsFile, $interfacePointer): void 34 | { 35 | $interfaceName = ClassHelper::getName($phpcsFile, $interfacePointer); 36 | 37 | $this->checkPrefix($phpcsFile, $interfacePointer, $interfaceName); 38 | $this->checkSuffix($phpcsFile, $interfacePointer, $interfaceName); 39 | } 40 | 41 | private function checkPrefix(File $phpcsFile, int $interfacePointer, string $interfaceName): void 42 | { 43 | $prefix = substr($interfaceName, 0, 9); 44 | 45 | if (strtolower($prefix) !== 'interface') { 46 | return; 47 | } 48 | 49 | $phpcsFile->addError(sprintf('Superfluous prefix "%s".', $prefix), $interfacePointer, self::CODE_SUPERFLUOUS_PREFIX); 50 | } 51 | 52 | private function checkSuffix(File $phpcsFile, int $interfacePointer, string $interfaceName): void 53 | { 54 | $suffix = substr($interfaceName, -9); 55 | 56 | if (strtolower($suffix) !== 'interface') { 57 | return; 58 | } 59 | 60 | $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $interfacePointer, self::CODE_SUPERFLUOUS_SUFFIX); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/SuperfluousTraitNamingSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return [ 24 | T_TRAIT, 25 | ]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $traitPointer 31 | */ 32 | public function process(File $phpcsFile, $traitPointer): void 33 | { 34 | $traitName = ClassHelper::getName($phpcsFile, $traitPointer); 35 | 36 | $this->checkSuffix($phpcsFile, $traitPointer, $traitName); 37 | } 38 | 39 | private function checkSuffix(File $phpcsFile, int $traitPointer, string $traitName): void 40 | { 41 | $suffix = substr($traitName, -5); 42 | 43 | if (strtolower($suffix) !== 'trait') { 44 | return; 45 | } 46 | 47 | $phpcsFile->addError(sprintf('Superfluous suffix "%s".', $suffix), $traitPointer, self::CODE_SUPERFLUOUS_SUFFIX); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/TraitUseDeclarationSniff.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | public function register(): array 28 | { 29 | return [ 30 | T_CLASS, 31 | T_ANON_CLASS, 32 | T_TRAIT, 33 | T_ENUM, 34 | ]; 35 | } 36 | 37 | /** 38 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 39 | * @param int $classPointer 40 | */ 41 | public function process(File $phpcsFile, $classPointer): void 42 | { 43 | $usePointers = ClassHelper::getTraitUsePointers($phpcsFile, $classPointer); 44 | 45 | foreach ($usePointers as $usePointer) { 46 | $this->checkDeclaration($phpcsFile, $usePointer); 47 | } 48 | } 49 | 50 | private function checkDeclaration(File $phpcsFile, int $usePointer): void 51 | { 52 | $commaPointer = TokenHelper::findNextLocal($phpcsFile, T_COMMA, $usePointer + 1); 53 | if ($commaPointer === null) { 54 | return; 55 | } 56 | 57 | $endPointer = TokenHelper::findNext($phpcsFile, [T_OPEN_CURLY_BRACKET, T_SEMICOLON], $usePointer + 1); 58 | 59 | $tokens = $phpcsFile->getTokens(); 60 | if ($tokens[$endPointer]['code'] === T_OPEN_CURLY_BRACKET) { 61 | $phpcsFile->addError( 62 | 'Multiple traits per use statement are forbidden.', 63 | $usePointer, 64 | self::CODE_MULTIPLE_TRAITS_PER_DECLARATION, 65 | ); 66 | return; 67 | } 68 | 69 | $fix = $phpcsFile->addFixableError( 70 | 'Multiple traits per use statement are forbidden.', 71 | $usePointer, 72 | self::CODE_MULTIPLE_TRAITS_PER_DECLARATION, 73 | ); 74 | 75 | if (!$fix) { 76 | return; 77 | } 78 | 79 | $indentation = ''; 80 | $currentPointer = $usePointer - 1; 81 | while ( 82 | $tokens[$currentPointer]['code'] === T_WHITESPACE 83 | && $tokens[$currentPointer]['content'] !== $phpcsFile->eolChar 84 | ) { 85 | $indentation .= $tokens[$currentPointer]['content']; 86 | $currentPointer--; 87 | } 88 | 89 | $phpcsFile->fixer->beginChangeset(); 90 | 91 | $otherCommaPointers = TokenHelper::findNextAll($phpcsFile, T_COMMA, $usePointer + 1, $endPointer); 92 | foreach ($otherCommaPointers as $otherCommaPointer) { 93 | $pointerAfterComma = TokenHelper::findNextEffective($phpcsFile, $otherCommaPointer + 1); 94 | 95 | FixerHelper::change($phpcsFile, $otherCommaPointer, $pointerAfterComma - 1, ';' . $phpcsFile->eolChar . $indentation . 'use '); 96 | } 97 | 98 | $phpcsFile->fixer->endChangeset(); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Classes/UnsupportedClassGroupException.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_STATIC, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $staticPointer 33 | */ 34 | public function process(File $phpcsFile, $staticPointer): void 35 | { 36 | $tokens = $phpcsFile->getTokens(); 37 | 38 | $doubleColonPointer = TokenHelper::findNextEffective($phpcsFile, $staticPointer + 1); 39 | if ($tokens[$doubleColonPointer]['code'] !== T_DOUBLE_COLON) { 40 | return; 41 | } 42 | 43 | $classPointer = null; 44 | foreach (array_reverse($tokens[$staticPointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) { 45 | if (!in_array($conditionTokenCode, Tokens::$ooScopeTokens, true)) { 46 | continue; 47 | } 48 | 49 | $classPointer = $conditionPointer; 50 | break; 51 | } 52 | 53 | if (!ClassHelper::isFinal($phpcsFile, $classPointer)) { 54 | return; 55 | } 56 | 57 | $fix = $phpcsFile->addFixableError( 58 | 'Useless late static binding because class is final.', 59 | $staticPointer, 60 | self::CODE_USELESS_LATE_STATIC_BINDING, 61 | ); 62 | 63 | if (!$fix) { 64 | return; 65 | } 66 | 67 | $phpcsFile->fixer->beginChangeset(); 68 | $phpcsFile->fixer->replaceToken($staticPointer, 'self'); 69 | $phpcsFile->fixer->endChangeset(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Commenting/DeprecatedAnnotationDeclarationSniff.php: -------------------------------------------------------------------------------- 1 | */ 19 | public function register(): array 20 | { 21 | return [T_DOC_COMMENT_OPEN_TAG]; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $docCommentStartPointer 27 | */ 28 | public function process(File $phpcsFile, $docCommentStartPointer): void 29 | { 30 | /** @var list> $annotations */ 31 | $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentStartPointer, '@deprecated'); 32 | 33 | if (count($annotations) === 0) { 34 | return; 35 | } 36 | 37 | foreach ($annotations as $annotation) { 38 | if ($annotation->getValue()->description !== '') { 39 | continue; 40 | } 41 | 42 | $phpcsFile->addError( 43 | 'Deprecated annotation must have a description.', 44 | $annotation->getStartPointer(), 45 | self::MISSING_DESCRIPTION, 46 | ); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Commenting/DisallowOneLinePropertyDocCommentSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [T_VARIABLE]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $propertyPointer 31 | */ 32 | public function process(File $phpcsFile, $propertyPointer): void 33 | { 34 | $tokens = $phpcsFile->getTokens(); 35 | 36 | // Not a property 37 | if (!PropertyHelper::isProperty($phpcsFile, $propertyPointer)) { 38 | return; 39 | } 40 | 41 | // Only validate properties with comment 42 | if (!DocCommentHelper::hasDocComment($phpcsFile, $propertyPointer)) { 43 | return; 44 | } 45 | 46 | /** @var int $docCommentStartPointer */ 47 | $docCommentStartPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $propertyPointer); 48 | $docCommentEndPointer = $tokens[$docCommentStartPointer]['comment_closer']; 49 | $lineDifference = $tokens[$docCommentEndPointer]['line'] - $tokens[$docCommentStartPointer]['line']; 50 | 51 | // Already multi-line 52 | if ($lineDifference !== 0) { 53 | return; 54 | } 55 | 56 | $fix = $phpcsFile->addFixableError( 57 | sprintf( 58 | 'Found one-line comment for property %s, use multi-line comment instead.', 59 | PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer), 60 | ), 61 | $docCommentStartPointer, 62 | self::CODE_ONE_LINE_PROPERTY_COMMENT, 63 | ); 64 | 65 | if (!$fix) { 66 | return; 67 | } 68 | 69 | $commentWhitespacePointer = TokenHelper::findPrevious($phpcsFile, [T_WHITESPACE], $docCommentStartPointer); 70 | $indent = ($commentWhitespacePointer !== null ? $tokens[$commentWhitespacePointer]['content'] : '') . ' '; 71 | 72 | $phpcsFile->fixer->beginChangeset(); 73 | 74 | $phpcsFile->fixer->addNewline($docCommentStartPointer); 75 | $phpcsFile->fixer->addContent($docCommentStartPointer, $indent); 76 | $phpcsFile->fixer->addContent($docCommentStartPointer, '*'); 77 | 78 | if ($docCommentEndPointer - 1 !== $docCommentStartPointer) { 79 | $phpcsFile->fixer->replaceToken( 80 | $docCommentEndPointer - 1, 81 | rtrim($phpcsFile->fixer->getTokenContent($docCommentEndPointer - 1), ' '), 82 | ); 83 | } 84 | 85 | $phpcsFile->fixer->addContentBefore($docCommentEndPointer, $indent); 86 | $phpcsFile->fixer->addNewlineBefore($docCommentEndPointer); 87 | 88 | $phpcsFile->fixer->endChangeset(); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Commenting/RequireOneLineDocCommentSniff.php: -------------------------------------------------------------------------------- 1 | addFixableError($error, $docCommentStartPointer, self::CODE_MULTI_LINE_DOC_COMMENT); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Commenting/RequireOneLinePropertyDocCommentSniff.php: -------------------------------------------------------------------------------- 1 | findNext(T_VARIABLE, $docCommentStartPointer); 53 | 54 | return $phpcsFile->addFixableError( 55 | sprintf($error, PropertyHelper::getFullyQualifiedName($phpcsFile, $propertyPointer)), 56 | $docCommentStartPointer, 57 | self::CODE_MULTI_LINE_PROPERTY_COMMENT, 58 | ); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/DisallowContinueWithoutIntegerOperandInSwitchSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_CONTINUE, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $continuePointer 32 | */ 33 | public function process(File $phpcsFile, $continuePointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $operandPointer = TokenHelper::findNextEffective($phpcsFile, $continuePointer + 1); 38 | 39 | if ($tokens[$operandPointer]['code'] === T_LNUMBER) { 40 | return; 41 | } 42 | 43 | $conditionTokenCode = current(array_reverse($tokens[$continuePointer]['conditions'])); 44 | if ($conditionTokenCode !== T_SWITCH) { 45 | return; 46 | } 47 | 48 | $fix = $phpcsFile->addFixableError( 49 | 'Usage of "continue" without integer operand in "switch" is disallowed, use "break" instead.', 50 | $continuePointer, 51 | self::CODE_DISALLOWED_CONTINUE_WITHOUT_INTEGER_OPERAND_IN_SWITCH, 52 | ); 53 | 54 | if (!$fix) { 55 | return; 56 | } 57 | 58 | $phpcsFile->fixer->beginChangeset(); 59 | $phpcsFile->fixer->replaceToken($continuePointer, 'break'); 60 | $phpcsFile->fixer->endChangeset(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/DisallowEmptySniff.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function register(): array 18 | { 19 | return [ 20 | T_EMPTY, 21 | ]; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $emptyPointer 27 | */ 28 | public function process(File $phpcsFile, $emptyPointer): void 29 | { 30 | $phpcsFile->addError('Use of empty() is disallowed.', $emptyPointer, self::CODE_DISALLOWED_EMPTY); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/DisallowNullSafeObjectOperatorSniff.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function register(): array 18 | { 19 | return [ 20 | T_NULLSAFE_OBJECT_OPERATOR, 21 | ]; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $operatorPointer 27 | */ 28 | public function process(File $phpcsFile, $operatorPointer): void 29 | { 30 | $phpcsFile->addError('Operator ?-> is disallowed.', $operatorPointer, self::CODE_DISALLOWED_NULL_SAFE_OBJECT_OPERATOR); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/DisallowShortTernaryOperatorSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_INLINE_THEN, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $inlineThenPointer 33 | */ 34 | public function process(File $phpcsFile, $inlineThenPointer): void 35 | { 36 | $tokens = $phpcsFile->getTokens(); 37 | 38 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); 39 | 40 | if ($tokens[$nextPointer]['code'] !== T_INLINE_ELSE) { 41 | return; 42 | } 43 | 44 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); 45 | 46 | $message = 'Use of short ternary operator is disallowed.'; 47 | 48 | if ($tokens[$previousPointer]['code'] !== T_VARIABLE) { 49 | $phpcsFile->addError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); 50 | return; 51 | } 52 | 53 | if (!$this->fixable) { 54 | $phpcsFile->addError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); 55 | return; 56 | } 57 | 58 | $fix = $phpcsFile->addFixableError($message, $inlineThenPointer, self::CODE_DISALLOWED_SHORT_TERNARY_OPERATOR); 59 | 60 | if (!$fix) { 61 | return; 62 | } 63 | 64 | $phpcsFile->fixer->beginChangeset(); 65 | 66 | $phpcsFile->fixer->addContent($inlineThenPointer, sprintf(' %s ', $tokens[$previousPointer]['content'])); 67 | 68 | $phpcsFile->fixer->endChangeset(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/DisallowYodaComparisonSniff.php: -------------------------------------------------------------------------------- 1 | (Foo::BAR, BAR) 22 | * > (true, false, null, 1, 1.0, arrays, 'foo') 23 | */ 24 | class DisallowYodaComparisonSniff implements Sniff 25 | { 26 | 27 | public const CODE_DISALLOWED_YODA_COMPARISON = 'DisallowedYodaComparison'; 28 | 29 | /** 30 | * @return array 31 | */ 32 | public function register(): array 33 | { 34 | return [ 35 | T_IS_IDENTICAL, 36 | T_IS_NOT_IDENTICAL, 37 | T_IS_EQUAL, 38 | T_IS_NOT_EQUAL, 39 | ]; 40 | } 41 | 42 | /** 43 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 44 | * @param int $comparisonTokenPointer 45 | */ 46 | public function process(File $phpcsFile, $comparisonTokenPointer): void 47 | { 48 | $tokens = $phpcsFile->getTokens(); 49 | $leftSideTokens = YodaHelper::getLeftSideTokens($tokens, $comparisonTokenPointer); 50 | $rightSideTokens = YodaHelper::getRightSideTokens($tokens, $comparisonTokenPointer); 51 | $leftDynamism = YodaHelper::getDynamismForTokens($tokens, $leftSideTokens); 52 | $rightDynamism = YodaHelper::getDynamismForTokens($tokens, $rightSideTokens); 53 | 54 | if ($leftDynamism === null || $rightDynamism === null) { 55 | return; 56 | } 57 | 58 | if ($leftDynamism >= $rightDynamism) { 59 | return; 60 | } 61 | 62 | if ($leftDynamism >= 900 && $rightDynamism >= 900) { 63 | return; 64 | } 65 | 66 | $errorParameters = [ 67 | 'Yoda comparisons are disallowed.', 68 | $comparisonTokenPointer, 69 | self::CODE_DISALLOWED_YODA_COMPARISON, 70 | ]; 71 | 72 | $lastRightSideTokenPointer = array_keys($rightSideTokens)[count($rightSideTokens) - 1]; 73 | 74 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $lastRightSideTokenPointer + 1); 75 | if ($tokens[$nextPointer]['code'] === T_EQUAL) { 76 | $phpcsFile->addError(...$errorParameters); 77 | return; 78 | } 79 | 80 | $fix = $phpcsFile->addFixableError(...$errorParameters); 81 | if (!$fix) { 82 | return; 83 | } 84 | 85 | YodaHelper::fix($phpcsFile, $leftSideTokens, $rightSideTokens); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/NewWithoutParenthesesSniff.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function register(): array 31 | { 32 | return [ 33 | T_NEW, 34 | ]; 35 | } 36 | 37 | /** 38 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 39 | * @param int $newPointer 40 | */ 41 | public function process(File $phpcsFile, $newPointer): void 42 | { 43 | $tokens = $phpcsFile->getTokens(); 44 | /** @var int $nextPointer */ 45 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $newPointer + 1); 46 | 47 | if ($tokens[$nextPointer]['code'] === T_ANON_CLASS) { 48 | return; 49 | } 50 | 51 | $parenthesisOpenerPointer = $nextPointer + 1; 52 | do { 53 | /** @var int $parenthesisOpenerPointer */ 54 | $parenthesisOpenerPointer = TokenHelper::findNext( 55 | $phpcsFile, 56 | [ 57 | T_OPEN_PARENTHESIS, 58 | T_SEMICOLON, 59 | T_COMMA, 60 | T_INLINE_THEN, 61 | T_INLINE_ELSE, 62 | T_COALESCE, 63 | T_CLOSE_SHORT_ARRAY, 64 | T_CLOSE_SQUARE_BRACKET, 65 | T_CLOSE_PARENTHESIS, 66 | T_DOUBLE_ARROW, 67 | ], 68 | $parenthesisOpenerPointer, 69 | ); 70 | 71 | if ( 72 | $tokens[$parenthesisOpenerPointer]['code'] !== T_CLOSE_SQUARE_BRACKET 73 | || $tokens[$parenthesisOpenerPointer]['bracket_opener'] <= $newPointer 74 | ) { 75 | break; 76 | } 77 | 78 | $parenthesisOpenerPointer++; 79 | } while (true); 80 | 81 | if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { 82 | return; 83 | } 84 | 85 | $nextPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $parenthesisOpenerPointer + 1); 86 | if ($nextPointer !== $tokens[$parenthesisOpenerPointer]['parenthesis_closer']) { 87 | return; 88 | } 89 | 90 | $fix = $phpcsFile->addFixableError('Useless parentheses in "new".', $newPointer, self::CODE_USELESS_PARENTHESES); 91 | if (!$fix) { 92 | return; 93 | } 94 | 95 | $phpcsFile->fixer->beginChangeset(); 96 | 97 | FixerHelper::removeBetweenIncluding( 98 | $phpcsFile, 99 | $parenthesisOpenerPointer, 100 | $tokens[$parenthesisOpenerPointer]['parenthesis_closer'], 101 | ); 102 | 103 | $phpcsFile->fixer->endChangeset(); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/RequireShortTernaryOperatorSniff.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function register(): array 26 | { 27 | return [ 28 | T_INLINE_THEN, 29 | ]; 30 | } 31 | 32 | /** 33 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 34 | * @param int $inlineThenPointer 35 | */ 36 | public function process(File $phpcsFile, $inlineThenPointer): void 37 | { 38 | $tokens = $phpcsFile->getTokens(); 39 | 40 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $inlineThenPointer + 1); 41 | 42 | if ($tokens[$nextPointer]['code'] === T_INLINE_ELSE) { 43 | return; 44 | } 45 | 46 | $conditionStartPointer = TernaryOperatorHelper::getStartPointer($phpcsFile, $inlineThenPointer); 47 | $inlineElsePointer = TernaryOperatorHelper::getElsePointer($phpcsFile, $inlineThenPointer); 48 | $inlineElseEndPointer = TernaryOperatorHelper::getEndPointer($phpcsFile, $inlineThenPointer, $inlineElsePointer); 49 | 50 | $thenContent = trim(TokenHelper::getContent($phpcsFile, $inlineThenPointer + 1, $inlineElsePointer - 1)); 51 | $elseContent = trim(TokenHelper::getContent($phpcsFile, $inlineElsePointer + 1, $inlineElseEndPointer)); 52 | 53 | $conditionEndPointer = TokenHelper::findPreviousEffective($phpcsFile, $inlineThenPointer - 1); 54 | 55 | $condition = TokenHelper::getContent($phpcsFile, $conditionStartPointer, $conditionEndPointer); 56 | 57 | if ($tokens[$conditionStartPointer]['code'] === T_BOOLEAN_NOT) { 58 | if ($elseContent !== ltrim($condition, '!')) { 59 | return; 60 | } 61 | } else { 62 | if ($thenContent !== $condition) { 63 | return; 64 | } 65 | } 66 | 67 | $fix = $phpcsFile->addFixableError('Use short ternary operator.', $inlineThenPointer, self::CODE_REQUIRED_SHORT_TERNARY_OPERATOR); 68 | 69 | if (!$fix) { 70 | return; 71 | } 72 | 73 | $phpcsFile->fixer->beginChangeset(); 74 | 75 | if ($tokens[$conditionStartPointer]['code'] === T_BOOLEAN_NOT) { 76 | $phpcsFile->fixer->replaceToken($conditionStartPointer, ''); 77 | 78 | FixerHelper::change($phpcsFile, $inlineThenPointer, $inlineElseEndPointer, sprintf('?: %s', $thenContent)); 79 | 80 | } else { 81 | FixerHelper::removeBetween($phpcsFile, $inlineThenPointer, $inlineElsePointer); 82 | } 83 | 84 | $phpcsFile->fixer->endChangeset(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/RequireYodaComparisonSniff.php: -------------------------------------------------------------------------------- 1 | (Foo::BAR, BAR) 19 | * > (true, false, null, 1, 1.0, arrays, 'foo') 20 | */ 21 | class RequireYodaComparisonSniff implements Sniff 22 | { 23 | 24 | public const CODE_REQUIRED_YODA_COMPARISON = 'RequiredYodaComparison'; 25 | 26 | public bool $alwaysVariableOnRight = false; 27 | 28 | /** 29 | * @return array 30 | */ 31 | public function register(): array 32 | { 33 | return [ 34 | T_IS_IDENTICAL, 35 | T_IS_NOT_IDENTICAL, 36 | T_IS_EQUAL, 37 | T_IS_NOT_EQUAL, 38 | ]; 39 | } 40 | 41 | /** 42 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 43 | * @param int $comparisonTokenPointer 44 | */ 45 | public function process(File $phpcsFile, $comparisonTokenPointer): void 46 | { 47 | $tokens = $phpcsFile->getTokens(); 48 | $leftSideTokens = YodaHelper::getLeftSideTokens($tokens, $comparisonTokenPointer); 49 | $rightSideTokens = YodaHelper::getRightSideTokens($tokens, $comparisonTokenPointer); 50 | $leftDynamism = YodaHelper::getDynamismForTokens($tokens, $leftSideTokens); 51 | $rightDynamism = YodaHelper::getDynamismForTokens($tokens, $rightSideTokens); 52 | 53 | if ($leftDynamism === null || $rightDynamism === null) { 54 | return; 55 | } 56 | 57 | if ($leftDynamism <= $rightDynamism) { 58 | return; 59 | } 60 | 61 | if (!$this->alwaysVariableOnRight && $leftDynamism >= 900 && $rightDynamism >= 900) { 62 | return; 63 | } 64 | 65 | $fix = $phpcsFile->addFixableError('Yoda comparison is required.', $comparisonTokenPointer, self::CODE_REQUIRED_YODA_COMPARISON); 66 | if (!$fix || count($leftSideTokens) === 0 || count($rightSideTokens) === 0) { 67 | return; 68 | } 69 | 70 | YodaHelper::fix($phpcsFile, $leftSideTokens, $rightSideTokens); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/ControlStructures/UnsupportedKeywordException.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function register(): array 21 | { 22 | return [ 23 | T_CATCH, 24 | ]; 25 | } 26 | 27 | /** 28 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 29 | * @param int $catchPointer 30 | */ 31 | public function process(File $phpcsFile, $catchPointer): void 32 | { 33 | $tokens = $phpcsFile->getTokens(); 34 | 35 | $catchToken = $tokens[$catchPointer]; 36 | $caughtTypes = CatchHelper::findCaughtTypesInCatch($phpcsFile, $catchToken); 37 | 38 | if (!in_array('\\Throwable', $caughtTypes, true)) { 39 | return; 40 | } 41 | 42 | $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $catchToken['scope_closer'] + 1); 43 | 44 | while ($nextCatchPointer !== null) { 45 | $nextCatchToken = $tokens[$nextCatchPointer]; 46 | if ($nextCatchToken['code'] !== T_CATCH) { 47 | break; 48 | } 49 | 50 | $phpcsFile->addError('Unreachable catch block.', $nextCatchPointer, self::CODE_CATCH_AFTER_THROWABLE_CATCH); 51 | 52 | $nextCatchPointer = TokenHelper::findNextEffective($phpcsFile, $nextCatchToken['scope_closer'] + 1); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Exceptions/DisallowNonCapturingCatchSniff.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function register(): array 20 | { 21 | return [ 22 | T_CATCH, 23 | ]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $catchPointer 29 | */ 30 | public function process(File $phpcsFile, $catchPointer): void 31 | { 32 | $tokens = $phpcsFile->getTokens(); 33 | 34 | $variablePointer = TokenHelper::findNext( 35 | $phpcsFile, 36 | T_VARIABLE, 37 | $tokens[$catchPointer]['parenthesis_opener'], 38 | $tokens[$catchPointer]['parenthesis_closer'], 39 | ); 40 | 41 | if ($variablePointer === null) { 42 | $phpcsFile->addError('Use of non-capturing catch is disallowed.', $catchPointer, self::CODE_DISALLOWED_NON_CAPTURING_CATCH); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Files/FileLengthSniff.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | public function register(): array 30 | { 31 | return [T_OPEN_TAG]; 32 | } 33 | 34 | /** 35 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 36 | * @param int $pointer 37 | */ 38 | public function process(File $phpcsFile, $pointer): void 39 | { 40 | $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); 41 | $flags = array_keys(array_filter([ 42 | FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, 43 | FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, 44 | ])); 45 | $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); 46 | 47 | $length = FunctionHelper::getLineCount($phpcsFile, $pointer, $flags); 48 | 49 | if ($length <= $this->maxLinesLength) { 50 | return; 51 | } 52 | 53 | $errorMessage = sprintf('Your file is too long. Currently using %d lines. Can be up to %d lines.', $length, $this->maxLinesLength); 54 | 55 | $phpcsFile->addError($errorMessage, $pointer, self::CODE_FILE_TOO_LONG); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Files/FilepathNamespaceExtractor.php: -------------------------------------------------------------------------------- 1 | */ 26 | private array $rootNamespaces; 27 | 28 | /** @var array dir(string) => true(bool) */ 29 | private array $skipDirs; 30 | 31 | /** @var list */ 32 | private array $extensions; 33 | 34 | /** 35 | * @param array $rootNamespaces directory(string) => namespace 36 | * @param list $skipDirs 37 | * @param list $extensions index(integer) => extension 38 | */ 39 | public function __construct(array $rootNamespaces, array $skipDirs, array $extensions) 40 | { 41 | $this->rootNamespaces = $rootNamespaces; 42 | $this->skipDirs = array_fill_keys($skipDirs, true); 43 | $this->extensions = array_map(static fn (string $extension): string => strtolower($extension), $extensions); 44 | } 45 | 46 | public function getTypeNameFromProjectPath(string $path): ?string 47 | { 48 | $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); 49 | if (!in_array($extension, $this->extensions, true)) { 50 | return null; 51 | } 52 | 53 | /** @var list $pathParts */ 54 | $pathParts = preg_split('~[/\\\]~', $path); 55 | $rootNamespace = null; 56 | while (count($pathParts) > 0) { 57 | array_shift($pathParts); 58 | foreach ($this->rootNamespaces as $directory => $namespace) { 59 | if (!StringHelper::startsWith(implode('/', $pathParts) . '/', $directory . '/')) { 60 | continue; 61 | } 62 | 63 | $directoryPartsCount = count(explode('/', $directory)); 64 | for ($i = 0; $i < $directoryPartsCount; $i++) { 65 | array_shift($pathParts); 66 | } 67 | 68 | $rootNamespace = $namespace; 69 | break 2; 70 | } 71 | } 72 | 73 | if ($rootNamespace === null) { 74 | return null; 75 | } 76 | 77 | array_unshift($pathParts, $rootNamespace); 78 | 79 | $typeName = implode('\\', array_filter($pathParts, fn (string $pathPart): bool => !isset($this->skipDirs[$pathPart]))); 80 | 81 | return substr($typeName, 0, -strlen('.' . $extension)); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowArrowFunctionSniff.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function register(): array 18 | { 19 | return [ 20 | T_FN, 21 | ]; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $arrowFunctionPointer 27 | */ 28 | public function process(File $phpcsFile, $arrowFunctionPointer): void 29 | { 30 | $phpcsFile->addError('Use of arrow function is disallowed.', $arrowFunctionPointer, self::CODE_DISALLOWED_ARROW_FUNCTION); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowEmptyFunctionSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return [T_FUNCTION]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $functionPointer 29 | */ 30 | public function process(File $phpcsFile, $functionPointer): void 31 | { 32 | $tokens = $phpcsFile->getTokens(); 33 | 34 | if (FunctionHelper::isAbstract($phpcsFile, $functionPointer)) { 35 | return; 36 | } 37 | 38 | if (FunctionHelper::getName($phpcsFile, $functionPointer) === '__construct') { 39 | $propertyPromotion = TokenHelper::findNext( 40 | $phpcsFile, 41 | Tokens::$scopeModifiers, 42 | $tokens[$functionPointer]['parenthesis_opener'] + 1, 43 | $tokens[$functionPointer]['parenthesis_closer'], 44 | ); 45 | 46 | if ($propertyPromotion !== null) { 47 | return; 48 | } 49 | } 50 | 51 | $firstContent = TokenHelper::findNextExcluding( 52 | $phpcsFile, 53 | T_WHITESPACE, 54 | $tokens[$functionPointer]['scope_opener'] + 1, 55 | $tokens[$functionPointer]['scope_closer'], 56 | ); 57 | 58 | if ($firstContent !== null) { 59 | return; 60 | } 61 | 62 | $phpcsFile->addError( 63 | 'Empty function body must have at least a comment to explain why is empty.', 64 | $functionPointer, 65 | self::CODE_EMPTY_FUNCTION, 66 | ); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowNamedArgumentsSniff.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function register(): array 19 | { 20 | return [ 21 | T_PARAM_NAME, 22 | ]; 23 | } 24 | 25 | /** 26 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 27 | * @param int $argumentNamePointer 28 | */ 29 | public function process(File $phpcsFile, $argumentNamePointer): void 30 | { 31 | $tokens = $phpcsFile->getTokens(); 32 | 33 | $phpcsFile->addError( 34 | sprintf('Named arguments are disallowed, usage of named argument "%s" found.', $tokens[$argumentNamePointer]['content']), 35 | $argumentNamePointer, 36 | self::CODE_DISALLOWED_NAMED_ARGUMENT, 37 | ); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInCallSniff.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | public function register(): array 32 | { 33 | return [ 34 | T_OPEN_PARENTHESIS, 35 | ]; 36 | } 37 | 38 | /** 39 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 40 | * @param int $parenthesisOpenerPointer 41 | */ 42 | public function process(File $phpcsFile, $parenthesisOpenerPointer): void 43 | { 44 | $tokens = $phpcsFile->getTokens(); 45 | 46 | if (array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) { 47 | return; 48 | } 49 | 50 | $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); 51 | if (!in_array( 52 | $tokens[$pointerBeforeParenthesisOpener]['code'], 53 | [...TokenHelper::ONLY_NAME_TOKEN_CODES, T_VARIABLE, T_ISSET, T_UNSET, T_CLOSE_PARENTHESIS, T_SELF, T_STATIC, T_PARENT], 54 | true, 55 | )) { 56 | return; 57 | } 58 | 59 | $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; 60 | $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); 61 | 62 | if ($tokens[$pointerBeforeParenthesisCloser]['code'] !== T_COMMA) { 63 | return; 64 | } 65 | 66 | if ($this->onlySingleLine && $tokens[$parenthesisOpenerPointer]['line'] !== $tokens[$parenthesisCloserPointer]['line']) { 67 | return; 68 | } 69 | 70 | $fix = $phpcsFile->addFixableError( 71 | 'Trailing comma after the last parameter in function call is disallowed.', 72 | $pointerBeforeParenthesisCloser, 73 | self::CODE_DISALLOWED_TRAILING_COMMA, 74 | ); 75 | 76 | if (!$fix) { 77 | return; 78 | } 79 | 80 | $phpcsFile->fixer->beginChangeset(); 81 | $phpcsFile->fixer->replaceToken($pointerBeforeParenthesisCloser, ''); 82 | 83 | if ($tokens[$pointerBeforeParenthesisCloser]['line'] === $tokens[$parenthesisCloserPointer]['line']) { 84 | FixerHelper::removeBetween($phpcsFile, $pointerBeforeParenthesisCloser, $parenthesisCloserPointer); 85 | } 86 | 87 | $phpcsFile->fixer->endChangeset(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInClosureUseSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return [ 27 | T_CLOSURE, 28 | ]; 29 | } 30 | 31 | /** 32 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 33 | * @param int $functionPointer 34 | */ 35 | public function process(File $phpcsFile, $functionPointer): void 36 | { 37 | $tokens = $phpcsFile->getTokens(); 38 | 39 | $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; 40 | 41 | $usePointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); 42 | 43 | if ($tokens[$usePointer]['code'] !== T_USE) { 44 | return; 45 | } 46 | 47 | $useParenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); 48 | $useParenthesisCloserPointer = $tokens[$useParenthesisOpenerPointer]['parenthesis_closer']; 49 | 50 | $pointerBeforeUseParenthesisCloser = TokenHelper::findPreviousExcluding( 51 | $phpcsFile, 52 | T_WHITESPACE, 53 | $tokens[$useParenthesisOpenerPointer]['parenthesis_closer'] - 1, 54 | $useParenthesisOpenerPointer, 55 | ); 56 | 57 | if ($tokens[$pointerBeforeUseParenthesisCloser]['code'] !== T_COMMA) { 58 | return; 59 | } 60 | 61 | if ($this->onlySingleLine && $tokens[$useParenthesisOpenerPointer]['line'] !== $tokens[$useParenthesisCloserPointer]['line']) { 62 | return; 63 | } 64 | 65 | $fix = $phpcsFile->addFixableError( 66 | 'Trailing comma after the last inherited variable in "use" of closure declaration is disallowed.', 67 | $pointerBeforeUseParenthesisCloser, 68 | self::CODE_DISALLOWED_TRAILING_COMMA, 69 | ); 70 | 71 | if (!$fix) { 72 | return; 73 | } 74 | 75 | $phpcsFile->fixer->beginChangeset(); 76 | $phpcsFile->fixer->replaceToken($pointerBeforeUseParenthesisCloser, ''); 77 | 78 | if ($tokens[$pointerBeforeUseParenthesisCloser]['line'] === $tokens[$useParenthesisCloserPointer]['line']) { 79 | FixerHelper::removeBetween($phpcsFile, $pointerBeforeUseParenthesisCloser, $useParenthesisCloserPointer); 80 | } 81 | 82 | $phpcsFile->fixer->endChangeset(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/DisallowTrailingCommaInDeclarationSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return TokenHelper::FUNCTION_TOKEN_CODES; 25 | } 26 | 27 | /** 28 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 29 | * @param int $functionPointer 30 | */ 31 | public function process(File $phpcsFile, $functionPointer): void 32 | { 33 | $tokens = $phpcsFile->getTokens(); 34 | 35 | $parenthesisOpenerPointer = $tokens[$functionPointer]['parenthesis_opener']; 36 | $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; 37 | 38 | $pointerBeforeParenthesisCloser = TokenHelper::findPreviousExcluding( 39 | $phpcsFile, 40 | T_WHITESPACE, 41 | $parenthesisCloserPointer - 1, 42 | $parenthesisOpenerPointer, 43 | ); 44 | 45 | if ($tokens[$pointerBeforeParenthesisCloser]['code'] !== T_COMMA) { 46 | return; 47 | } 48 | 49 | if ($this->onlySingleLine && $tokens[$parenthesisOpenerPointer]['line'] !== $tokens[$parenthesisCloserPointer]['line']) { 50 | return; 51 | } 52 | 53 | $fix = $phpcsFile->addFixableError( 54 | 'Trailing comma after the last parameter in function declaration is disallowed.', 55 | $pointerBeforeParenthesisCloser, 56 | self::CODE_DISALLOWED_TRAILING_COMMA, 57 | ); 58 | 59 | if (!$fix) { 60 | return; 61 | } 62 | 63 | $phpcsFile->fixer->beginChangeset(); 64 | $phpcsFile->fixer->replaceToken($pointerBeforeParenthesisCloser, ''); 65 | 66 | if ($tokens[$pointerBeforeParenthesisCloser]['line'] === $tokens[$parenthesisCloserPointer]['line']) { 67 | FixerHelper::removeBetween($phpcsFile, $pointerBeforeParenthesisCloser, $parenthesisCloserPointer); 68 | } 69 | 70 | $phpcsFile->fixer->endChangeset(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/FunctionLengthSniff.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | public function register(): array 30 | { 31 | return [T_FUNCTION]; 32 | } 33 | 34 | /** 35 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 36 | * @param int $functionPointer 37 | */ 38 | public function process(File $file, $functionPointer): void 39 | { 40 | $this->maxLinesLength = SniffSettingsHelper::normalizeInteger($this->maxLinesLength); 41 | 42 | $flags = array_keys(array_filter([ 43 | FunctionHelper::LINE_INCLUDE_COMMENT => $this->includeComments, 44 | FunctionHelper::LINE_INCLUDE_WHITESPACE => $this->includeWhitespace, 45 | ])); 46 | $flags = array_reduce($flags, static fn ($carry, $flag): int => $carry | $flag, 0); 47 | 48 | $length = FunctionHelper::getFunctionLengthInLines($file, $functionPointer, $flags); 49 | 50 | if ($length <= $this->maxLinesLength) { 51 | return; 52 | } 53 | 54 | $errorMessage = sprintf( 55 | 'Your function is too long. Currently using %d lines. Can be up to %d lines.', 56 | $length, 57 | $this->maxLinesLength, 58 | ); 59 | 60 | $file->addError($errorMessage, $functionPointer, self::CODE_FUNCTION_LENGTH); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/NamedArgumentSpacingSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_PARAM_NAME, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $pointer 32 | */ 33 | public function process(File $phpcsFile, $pointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | /** @var int $colonPointer */ 38 | $colonPointer = TokenHelper::findNext($phpcsFile, T_COLON, $pointer + 1); 39 | 40 | $parameterName = $tokens[$pointer]['content']; 41 | 42 | if ($colonPointer !== $pointer + 1) { 43 | $fix = $phpcsFile->addFixableError( 44 | sprintf('There must be no whitespace between named argument "%s" and colon.', $parameterName), 45 | $colonPointer, 46 | self::CODE_WHITESPACE_BEFORE_COLON, 47 | ); 48 | if ($fix) { 49 | $phpcsFile->fixer->replaceToken($colonPointer - 1, ''); 50 | } 51 | } 52 | 53 | $whitespacePointer = $colonPointer + 1; 54 | 55 | if ( 56 | $tokens[$whitespacePointer]['code'] === T_WHITESPACE 57 | && $tokens[$whitespacePointer]['content'] === ' ' 58 | ) { 59 | return; 60 | } 61 | 62 | $fix = $phpcsFile->addFixableError( 63 | sprintf('There must be exactly one space after colon in named argument "%s".', $parameterName), 64 | $colonPointer, 65 | self::CODE_NO_WHITESPACE_AFTER_COLON, 66 | ); 67 | 68 | if (!$fix) { 69 | return; 70 | } 71 | 72 | if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) { 73 | $phpcsFile->fixer->replaceToken($whitespacePointer, ' '); 74 | } else { 75 | $phpcsFile->fixer->addContent($colonPointer, ' '); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInCallSniff.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | public function register(): array 32 | { 33 | return [ 34 | T_OPEN_PARENTHESIS, 35 | ]; 36 | } 37 | 38 | /** 39 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 40 | * @param int $parenthesisOpenerPointer 41 | */ 42 | public function process(File $phpcsFile, $parenthesisOpenerPointer): void 43 | { 44 | $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70300); 45 | 46 | if (!$this->enable) { 47 | return; 48 | } 49 | 50 | $tokens = $phpcsFile->getTokens(); 51 | 52 | if (array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])) { 53 | return; 54 | } 55 | 56 | $pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1); 57 | if (!in_array( 58 | $tokens[$pointerBeforeParenthesisOpener]['code'], 59 | [...TokenHelper::ONLY_NAME_TOKEN_CODES, T_VARIABLE, T_ISSET, T_UNSET, T_CLOSE_PARENTHESIS, T_SELF, T_STATIC, T_PARENT], 60 | true, 61 | )) { 62 | return; 63 | } 64 | 65 | $parenthesisCloserPointer = $tokens[$parenthesisOpenerPointer]['parenthesis_closer']; 66 | 67 | if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { 68 | return; 69 | } 70 | 71 | $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisCloserPointer - 1); 72 | if ($pointerBeforeParenthesisCloser === $parenthesisOpenerPointer) { 73 | return; 74 | } 75 | 76 | if ($tokens[$parenthesisCloserPointer]['line'] === $tokens[$pointerBeforeParenthesisCloser]['line']) { 77 | return; 78 | } 79 | 80 | if ($tokens[$pointerBeforeParenthesisCloser]['code'] === T_COMMA) { 81 | return; 82 | } 83 | 84 | $fix = $phpcsFile->addFixableError( 85 | 'Multi-line function calls must have a trailing comma after the last parameter.', 86 | $pointerBeforeParenthesisCloser, 87 | self::CODE_MISSING_TRAILING_COMMA, 88 | ); 89 | 90 | if (!$fix) { 91 | return; 92 | } 93 | 94 | $phpcsFile->fixer->beginChangeset(); 95 | $phpcsFile->fixer->addContent($pointerBeforeParenthesisCloser, ','); 96 | $phpcsFile->fixer->endChangeset(); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInClosureUseSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return [T_CLOSURE]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $functionPointer 32 | */ 33 | public function process(File $phpcsFile, $functionPointer): void 34 | { 35 | $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); 36 | 37 | if (!$this->enable) { 38 | return; 39 | } 40 | 41 | $tokens = $phpcsFile->getTokens(); 42 | 43 | $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; 44 | 45 | $usePointer = TokenHelper::findNextEffective($phpcsFile, $parenthesisCloserPointer + 1); 46 | 47 | if ($tokens[$usePointer]['code'] !== T_USE) { 48 | return; 49 | } 50 | 51 | $useParenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); 52 | $useParenthesisCloserPointer = $tokens[$useParenthesisOpenerPointer]['parenthesis_closer']; 53 | 54 | if ($tokens[$useParenthesisOpenerPointer]['line'] === $tokens[$useParenthesisCloserPointer]['line']) { 55 | return; 56 | } 57 | 58 | $pointerBeforeUseParenthesisCloser = TokenHelper::findPreviousExcluding( 59 | $phpcsFile, 60 | T_WHITESPACE, 61 | $useParenthesisCloserPointer - 1, 62 | $useParenthesisOpenerPointer, 63 | ); 64 | 65 | if ($tokens[$pointerBeforeUseParenthesisCloser]['code'] === T_COMMA) { 66 | return; 67 | } 68 | 69 | $fix = $phpcsFile->addFixableError( 70 | 'Multi-line "use" of closure declaration must have a trailing comma after the last inherited variable.', 71 | $pointerBeforeUseParenthesisCloser, 72 | self::CODE_MISSING_TRAILING_COMMA, 73 | ); 74 | 75 | if (!$fix) { 76 | return; 77 | } 78 | 79 | $phpcsFile->fixer->beginChangeset(); 80 | $phpcsFile->fixer->addContent($pointerBeforeUseParenthesisCloser, ','); 81 | $phpcsFile->fixer->endChangeset(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/RequireTrailingCommaInDeclarationSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return TokenHelper::FUNCTION_TOKEN_CODES; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $functionPointer 29 | */ 30 | public function process(File $phpcsFile, $functionPointer): void 31 | { 32 | $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80000); 33 | 34 | if (!$this->enable) { 35 | return; 36 | } 37 | 38 | $tokens = $phpcsFile->getTokens(); 39 | 40 | $parenthesisOpenerPointer = $tokens[$functionPointer]['parenthesis_opener']; 41 | $parenthesisCloserPointer = $tokens[$functionPointer]['parenthesis_closer']; 42 | 43 | if ($tokens[$parenthesisOpenerPointer]['line'] === $tokens[$parenthesisCloserPointer]['line']) { 44 | return; 45 | } 46 | 47 | $pointerBeforeParenthesisCloser = TokenHelper::findPreviousEffective( 48 | $phpcsFile, 49 | $parenthesisCloserPointer - 1, 50 | $parenthesisOpenerPointer, 51 | ); 52 | 53 | if ($pointerBeforeParenthesisCloser === $parenthesisOpenerPointer) { 54 | return; 55 | } 56 | 57 | if ($tokens[$pointerBeforeParenthesisCloser]['code'] === T_COMMA) { 58 | return; 59 | } 60 | 61 | $fix = $phpcsFile->addFixableError( 62 | 'Multi-line function declaration must have a trailing comma after the last parameter.', 63 | $pointerBeforeParenthesisCloser, 64 | self::CODE_MISSING_TRAILING_COMMA, 65 | ); 66 | 67 | if (!$fix) { 68 | return; 69 | } 70 | 71 | $phpcsFile->fixer->beginChangeset(); 72 | $phpcsFile->fixer->addContent($pointerBeforeParenthesisCloser, ','); 73 | $phpcsFile->fixer->endChangeset(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/StaticClosureSniff.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | public function register(): array 27 | { 28 | return [ 29 | T_CLOSURE, 30 | T_FN, 31 | ]; 32 | } 33 | 34 | /** 35 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 36 | * @param int $closurePointer 37 | */ 38 | public function process(File $phpcsFile, $closurePointer): void 39 | { 40 | $tokens = $phpcsFile->getTokens(); 41 | 42 | $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $closurePointer - 1); 43 | if ($tokens[$previousPointer]['code'] === T_STATIC) { 44 | return; 45 | } 46 | 47 | if ($tokens[$previousPointer]['code'] === T_OPEN_PARENTHESIS) { 48 | $pointerBeforeParenthesis = TokenHelper::findPreviousEffective($phpcsFile, $previousPointer - 1); 49 | if ( 50 | $tokens[$pointerBeforeParenthesis]['code'] === T_STRING 51 | && $tokens[$pointerBeforeParenthesis]['content'] === 'bind' 52 | ) { 53 | return; 54 | } 55 | } 56 | 57 | $closureScopeOpenerPointer = $tokens[$closurePointer]['scope_opener']; 58 | $closureScopeCloserPointer = $tokens[$closurePointer]['scope_closer']; 59 | 60 | $thisPointer = TokenHelper::findNextContent( 61 | $phpcsFile, 62 | T_VARIABLE, 63 | '$this', 64 | $closureScopeOpenerPointer + 1, 65 | $closureScopeCloserPointer, 66 | ); 67 | if ($thisPointer !== null) { 68 | return; 69 | } 70 | 71 | $stringPointers = TokenHelper::findNextAll( 72 | $phpcsFile, 73 | T_DOUBLE_QUOTED_STRING, 74 | $closureScopeOpenerPointer + 1, 75 | $closureScopeCloserPointer, 76 | ); 77 | foreach ($stringPointers as $stringPointer) { 78 | if (VariableHelper::isUsedInScopeInString($phpcsFile, '$this', $stringPointer)) { 79 | return; 80 | } 81 | } 82 | 83 | $parentPointer = TokenHelper::findNext($phpcsFile, T_PARENT, $closureScopeOpenerPointer + 1, $closureScopeCloserPointer); 84 | if ($parentPointer !== null) { 85 | return; 86 | } 87 | 88 | $fix = $phpcsFile->addFixableError( 89 | 'Closure not using "$this" should be declared static.', 90 | $closurePointer, 91 | self::CODE_CLOSURE_NOT_STATIC, 92 | ); 93 | 94 | if (!$fix) { 95 | return; 96 | } 97 | 98 | $phpcsFile->fixer->beginChangeset(); 99 | $phpcsFile->fixer->addContentBefore($closurePointer, 'static '); 100 | $phpcsFile->fixer->endChangeset(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Functions/UselessParameterDefaultValueSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return TokenHelper::FUNCTION_TOKEN_CODES; 25 | } 26 | 27 | /** 28 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 29 | * @param int $functionPointer 30 | */ 31 | public function process(File $phpcsFile, $functionPointer): void 32 | { 33 | $parameters = $phpcsFile->getMethodParameters($functionPointer); 34 | $parametersCount = count($parameters); 35 | 36 | if ($parametersCount === 0) { 37 | return; 38 | } 39 | 40 | for ($i = 0; $i < $parametersCount; $i++) { 41 | $parameter = $parameters[$i]; 42 | 43 | if (!array_key_exists('default', $parameter)) { 44 | continue; 45 | } 46 | 47 | $defaultValue = strtolower($parameter['default']); 48 | if ($defaultValue === 'null' && !$parameter['nullable_type']) { 49 | continue; 50 | } 51 | 52 | for ($j = $i + 1; $j < $parametersCount; $j++) { 53 | $nextParameter = $parameters[$j]; 54 | 55 | if (array_key_exists('default', $nextParameter)) { 56 | continue; 57 | } 58 | 59 | if ($nextParameter['variable_length']) { 60 | break; 61 | } 62 | 63 | $fix = $phpcsFile->addFixableError( 64 | sprintf('Useless default value of parameter %s.', $parameter['name']), 65 | $parameter['token'], 66 | self::CODE_USELESS_PARAMETER_DEFAULT_VALUE, 67 | ); 68 | 69 | if (!$fix) { 70 | continue; 71 | } 72 | 73 | $commaPointer = TokenHelper::findPrevious($phpcsFile, T_COMMA, $parameters[$i + 1]['token'] - 1); 74 | /** @var int $parameterPointer */ 75 | $parameterPointer = $parameter['token']; 76 | 77 | $phpcsFile->fixer->beginChangeset(); 78 | for ($k = $parameterPointer + 1; $k < $commaPointer; $k++) { 79 | $phpcsFile->fixer->replaceToken($k, ''); 80 | } 81 | $phpcsFile->fixer->endChangeset(); 82 | 83 | break; 84 | } 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/DisallowGroupUseSniff.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function register(): array 18 | { 19 | return [ 20 | T_OPEN_USE_GROUP, 21 | ]; 22 | } 23 | 24 | /** 25 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 26 | * @param int $usePointer 27 | */ 28 | public function process(File $phpcsFile, $usePointer): void 29 | { 30 | $phpcsFile->addError( 31 | 'Group use declaration is disallowed, use single use for every import.', 32 | $usePointer, 33 | self::CODE_DISALLOWED_GROUP_USE, 34 | ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalConstantsSniff.php: -------------------------------------------------------------------------------- 1 | isConstant(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedGlobalFunctionsSniff.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | protected function getNormalizedInclude(): array 19 | { 20 | $include = parent::getNormalizedInclude(); 21 | 22 | if ($this->includeSpecialFunctions) { 23 | array_push($include, ...FunctionHelper::SPECIAL_FUNCTIONS); 24 | } 25 | 26 | return $include; 27 | } 28 | 29 | protected function getNotFullyQualifiedMessage(): string 30 | { 31 | return 'Function %s() should be referenced via a fully qualified name.'; 32 | } 33 | 34 | protected function isCaseSensitive(): bool 35 | { 36 | return false; 37 | } 38 | 39 | protected function isValidType(ReferencedName $name): bool 40 | { 41 | return $name->isFunction(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/MultipleUsesPerLineSniff.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | public function register(): array 22 | { 23 | return [ 24 | T_USE, 25 | ]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $usePointer 31 | */ 32 | public function process(File $phpcsFile, $usePointer): void 33 | { 34 | if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { 35 | return; 36 | } 37 | 38 | $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $usePointer + 1); 39 | $commaPointer = TokenHelper::findNext($phpcsFile, T_COMMA, $usePointer + 1, $endPointer); 40 | 41 | if ($commaPointer === null) { 42 | return; 43 | } 44 | 45 | $phpcsFile->addError('Multiple used types per use statement are forbidden.', $commaPointer, self::CODE_MULTIPLE_USES_PER_LINE); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/RequireOneNamespaceInFileSniff.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function register(): array 20 | { 21 | return [ 22 | T_NAMESPACE, 23 | ]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $namespacePointer 29 | */ 30 | public function process(File $phpcsFile, $namespacePointer): void 31 | { 32 | $tokens = $phpcsFile->getTokens(); 33 | 34 | $pointerAfterNamespace = TokenHelper::findNextEffective($phpcsFile, $namespacePointer + 1); 35 | if ($tokens[$pointerAfterNamespace]['code'] === T_NS_SEPARATOR) { 36 | return; 37 | } 38 | 39 | $previousNamespacePointer = $namespacePointer; 40 | do { 41 | $previousNamespacePointer = TokenHelper::findPrevious($phpcsFile, T_NAMESPACE, $previousNamespacePointer - 1); 42 | if ($previousNamespacePointer === null) { 43 | return; 44 | } 45 | 46 | $pointerAfterPreviousNamespace = TokenHelper::findNextEffective($phpcsFile, $previousNamespacePointer + 1); 47 | if ($tokens[$pointerAfterPreviousNamespace]['code'] === T_NS_SEPARATOR) { 48 | continue; 49 | } 50 | 51 | break; 52 | 53 | } while (true); 54 | 55 | $phpcsFile->addError('Only one namespace in a file is allowed.', $namespacePointer, self::CODE_MORE_NAMESPACES_IN_FILE); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/UseDoesNotStartWithBackslashSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_USE, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $usePointer 32 | */ 33 | public function process(File $phpcsFile, $usePointer): void 34 | { 35 | if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { 36 | return; 37 | } 38 | 39 | $tokens = $phpcsFile->getTokens(); 40 | 41 | /** @var int $nextTokenPointer */ 42 | $nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1); 43 | 44 | if ( 45 | in_array($tokens[$nextTokenPointer]['code'], TokenHelper::ONLY_NAME_TOKEN_CODES, true) 46 | && ( 47 | $tokens[$nextTokenPointer]['content'] === 'function' 48 | || $tokens[$nextTokenPointer]['content'] === 'const' 49 | ) 50 | ) { 51 | /** @var int $nextTokenPointer */ 52 | $nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $nextTokenPointer + 1); 53 | } 54 | 55 | if (!NamespaceHelper::isFullyQualifiedPointer($phpcsFile, $nextTokenPointer)) { 56 | return; 57 | } 58 | 59 | $fix = $phpcsFile->addFixableError( 60 | 'Use statement cannot start with a backslash.', 61 | $nextTokenPointer, 62 | self::CODE_STARTS_WITH_BACKSLASH, 63 | ); 64 | 65 | if (!$fix) { 66 | return; 67 | } 68 | 69 | $phpcsFile->fixer->beginChangeset(); 70 | $phpcsFile->fixer->replaceToken($nextTokenPointer, ltrim($tokens[$nextTokenPointer]['content'], '\\')); 71 | $phpcsFile->fixer->endChangeset(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/UseFromSameNamespaceSniff.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | public function register(): array 28 | { 29 | return [ 30 | T_USE, 31 | ]; 32 | } 33 | 34 | /** 35 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 36 | * @param int $usePointer 37 | */ 38 | public function process(File $phpcsFile, $usePointer): void 39 | { 40 | if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { 41 | return; 42 | } 43 | 44 | $namespaceName = NamespaceHelper::findCurrentNamespaceName($phpcsFile, $usePointer); 45 | if ($namespaceName === null) { 46 | $namespaceName = ''; 47 | } 48 | 49 | $usedTypeName = UseStatementHelper::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer); 50 | if (!StringHelper::startsWith($usedTypeName, $namespaceName)) { 51 | return; 52 | } 53 | 54 | $asPointer = $this->findAsPointer($phpcsFile, $usePointer); 55 | if ($asPointer !== null) { 56 | return; 57 | } 58 | 59 | $usedTypeNameRest = substr($usedTypeName, strlen($namespaceName)); 60 | if (!NamespaceHelper::isFullyQualifiedName($usedTypeNameRest) && $namespaceName !== '') { 61 | return; 62 | } 63 | 64 | if (NamespaceHelper::hasNamespace($usedTypeNameRest)) { 65 | return; 66 | } 67 | 68 | $fix = $phpcsFile->addFixableError(sprintf( 69 | 'Use %s is from the same namespace – that is prohibited.', 70 | $usedTypeName, 71 | ), $usePointer, self::CODE_USE_FROM_SAME_NAMESPACE); 72 | if (!$fix) { 73 | return; 74 | } 75 | 76 | $endPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $usePointer) + 1; 77 | 78 | $phpcsFile->fixer->beginChangeset(); 79 | 80 | FixerHelper::removeBetweenIncluding($phpcsFile, $usePointer, $endPointer); 81 | 82 | $phpcsFile->fixer->endChangeset(); 83 | } 84 | 85 | private function findAsPointer(File $phpcsFile, int $startPointer): ?int 86 | { 87 | return TokenHelper::findNextLocal($phpcsFile, T_AS, $startPointer); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/UseOnlyWhitelistedNamespacesSniff.php: -------------------------------------------------------------------------------- 1 | */ 21 | public array $namespacesRequiredToUse = []; 22 | 23 | /** @var list|null */ 24 | private ?array $normalizedNamespacesRequiredToUse = null; 25 | 26 | /** 27 | * @return array 28 | */ 29 | public function register(): array 30 | { 31 | return [ 32 | T_USE, 33 | ]; 34 | } 35 | 36 | /** 37 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 38 | * @param int $usePointer 39 | */ 40 | public function process(File $phpcsFile, $usePointer): void 41 | { 42 | if (!UseStatementHelper::isImportUse($phpcsFile, $usePointer)) { 43 | return; 44 | } 45 | 46 | $className = UseStatementHelper::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer); 47 | if ($this->allowUseFromRootNamespace && !NamespaceHelper::isQualifiedName($className)) { 48 | return; 49 | } 50 | 51 | foreach ($this->getNamespacesRequiredToUse() as $namespace) { 52 | if (!NamespaceHelper::isTypeInNamespace($className, $namespace)) { 53 | continue; 54 | } 55 | 56 | return; 57 | } 58 | 59 | $phpcsFile->addError(sprintf( 60 | 'Type %s should not be used, but referenced via a fully qualified name.', 61 | $className, 62 | ), $usePointer, self::CODE_NON_FULLY_QUALIFIED); 63 | } 64 | 65 | /** 66 | * @return list 67 | */ 68 | private function getNamespacesRequiredToUse(): array 69 | { 70 | if ($this->normalizedNamespacesRequiredToUse === null) { 71 | $this->normalizedNamespacesRequiredToUse = SniffSettingsHelper::normalizeArray($this->namespacesRequiredToUse); 72 | } 73 | 74 | return $this->normalizedNamespacesRequiredToUse; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Namespaces/UselessAliasSniff.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function register(): array 26 | { 27 | return [ 28 | T_OPEN_TAG, 29 | ]; 30 | } 31 | 32 | /** 33 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 34 | * @param int $openTagPointer 35 | */ 36 | public function process(File $phpcsFile, $openTagPointer): void 37 | { 38 | if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) { 39 | return; 40 | } 41 | 42 | $fileUseStatements = UseStatementHelper::getFileUseStatements($phpcsFile); 43 | 44 | if (count($fileUseStatements) === 0) { 45 | return; 46 | } 47 | 48 | foreach ($fileUseStatements as $useStatements) { 49 | foreach ($useStatements as $useStatement) { 50 | if ($useStatement->getAlias() === null) { 51 | continue; 52 | } 53 | 54 | $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($useStatement->getFullyQualifiedTypeName()); 55 | if ($unqualifiedName !== $useStatement->getAlias()) { 56 | continue; 57 | } 58 | 59 | $fix = $phpcsFile->addFixableError( 60 | sprintf('Useless alias "%s" for use of "%s".', $useStatement->getAlias(), $useStatement->getFullyQualifiedTypeName()), 61 | $useStatement->getPointer(), 62 | self::CODE_USELESS_ALIAS, 63 | ); 64 | 65 | if (!$fix) { 66 | continue; 67 | } 68 | 69 | $asPointer = TokenHelper::findNext($phpcsFile, T_AS, $useStatement->getPointer() + 1); 70 | $nameEndPointer = TokenHelper::findPrevious($phpcsFile, TokenHelper::ONLY_NAME_TOKEN_CODES, $asPointer - 1); 71 | $useSemicolonPointer = TokenHelper::findNext($phpcsFile, T_SEMICOLON, $asPointer + 1); 72 | 73 | $phpcsFile->fixer->beginChangeset(); 74 | 75 | FixerHelper::removeBetween($phpcsFile, $nameEndPointer, $useSemicolonPointer); 76 | 77 | $phpcsFile->fixer->endChangeset(); 78 | } 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Numbers/DisallowNumericLiteralSeparatorSniff.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function register(): array 21 | { 22 | return [ 23 | T_LNUMBER, 24 | T_DNUMBER, 25 | ]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $numberPointer 31 | */ 32 | public function process(File $phpcsFile, $numberPointer): void 33 | { 34 | $tokens = $phpcsFile->getTokens(); 35 | 36 | if (strpos($tokens[$numberPointer]['content'], '_') === false) { 37 | return; 38 | } 39 | 40 | $fix = $phpcsFile->addFixableError( 41 | 'Use of numeric literal separator is disallowed.', 42 | $numberPointer, 43 | self::CODE_DISALLOWED_NUMERIC_LITERAL_SEPARATOR, 44 | ); 45 | 46 | if (!$fix) { 47 | return; 48 | } 49 | 50 | $phpcsFile->fixer->beginChangeset(); 51 | $phpcsFile->fixer->replaceToken($numberPointer, str_replace('_', '', $tokens[$numberPointer]['content'])); 52 | $phpcsFile->fixer->endChangeset(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Numbers/RequireNumericLiteralSeparatorSniff.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | public function register(): array 30 | { 31 | return [ 32 | T_LNUMBER, 33 | T_DNUMBER, 34 | ]; 35 | } 36 | 37 | /** 38 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 39 | * @param int $numberPointer 40 | */ 41 | public function process(File $phpcsFile, $numberPointer): void 42 | { 43 | $this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 70400); 44 | $this->minDigitsBeforeDecimalPoint = SniffSettingsHelper::normalizeInteger($this->minDigitsBeforeDecimalPoint); 45 | $this->minDigitsAfterDecimalPoint = SniffSettingsHelper::normalizeInteger($this->minDigitsAfterDecimalPoint); 46 | 47 | if (!$this->enable) { 48 | return; 49 | } 50 | 51 | $tokens = $phpcsFile->getTokens(); 52 | $number = $tokens[$numberPointer]['content']; 53 | 54 | if (strpos($tokens[$numberPointer]['content'], '_') !== false) { 55 | return; 56 | } 57 | 58 | if ( 59 | $this->ignoreOctalNumbers 60 | && preg_match('~^0[0-7]+$~', $number) === 1 61 | ) { 62 | return; 63 | } 64 | 65 | $regexp = '~(?:^\\d{' . $this->minDigitsBeforeDecimalPoint . '}|\.\\d{' . $this->minDigitsAfterDecimalPoint . '})~'; 66 | 67 | if (preg_match($regexp, $number) === 0) { 68 | return; 69 | } 70 | 71 | $phpcsFile->addError( 72 | 'Use of numeric literal separator is required.', 73 | $numberPointer, 74 | self::CODE_REQUIRED_NUMERIC_LITERAL_SEPARATOR, 75 | ); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Operators/DisallowEqualOperatorsSniff.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function register(): array 21 | { 22 | return [ 23 | T_IS_EQUAL, 24 | T_IS_NOT_EQUAL, 25 | ]; 26 | } 27 | 28 | /** 29 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 30 | * @param int $operatorPointer 31 | */ 32 | public function process(File $phpcsFile, $operatorPointer): void 33 | { 34 | $tokens = $phpcsFile->getTokens(); 35 | 36 | if ($tokens[$operatorPointer]['code'] === T_IS_EQUAL) { 37 | $fix = $phpcsFile->addFixableError( 38 | 'Operator == is disallowed, use === instead.', 39 | $operatorPointer, 40 | self::CODE_DISALLOWED_EQUAL_OPERATOR, 41 | ); 42 | if ($fix) { 43 | $phpcsFile->fixer->beginChangeset(); 44 | $phpcsFile->fixer->replaceToken($operatorPointer, '==='); 45 | $phpcsFile->fixer->endChangeset(); 46 | } 47 | } else { 48 | $fix = $phpcsFile->addFixableError(sprintf( 49 | 'Operator %s is disallowed, use !== instead.', 50 | $tokens[$operatorPointer]['content'], 51 | ), $operatorPointer, self::CODE_DISALLOWED_NOT_EQUAL_OPERATOR); 52 | if ($fix) { 53 | $phpcsFile->fixer->beginChangeset(); 54 | $phpcsFile->fixer->replaceToken($operatorPointer, '!=='); 55 | $phpcsFile->fixer->endChangeset(); 56 | } 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Operators/DisallowIncrementAndDecrementOperatorsSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_DEC, 27 | T_INC, 28 | ]; 29 | } 30 | 31 | /** 32 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 33 | * @param int $operatorPointer 34 | */ 35 | public function process(File $phpcsFile, $operatorPointer): void 36 | { 37 | $tokens = $phpcsFile->getTokens(); 38 | 39 | /** @var int $nextPointer */ 40 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $operatorPointer + 1); 41 | $afterVariableEndPointer = IdentificatorHelper::findEndPointer($phpcsFile, $nextPointer); 42 | $isPostOperator = $afterVariableEndPointer === null; 43 | 44 | if ($tokens[$operatorPointer]['code'] === T_INC) { 45 | if ($isPostOperator) { 46 | $code = self::CODE_DISALLOWED_POST_INCREMENT_OPERATOR; 47 | $message = 'Use of post-increment operator is disallowed.'; 48 | } else { 49 | $code = self::CODE_DISALLOWED_PRE_INCREMENT_OPERATOR; 50 | $message = 'Use of pre-increment operator is disallowed.'; 51 | } 52 | } else { 53 | if ($isPostOperator) { 54 | $code = self::CODE_DISALLOWED_POST_DECREMENT_OPERATOR; 55 | $message = 'Use of post-decrement operator is disallowed.'; 56 | } else { 57 | $code = self::CODE_DISALLOWED_PRE_DECREMENT_OPERATOR; 58 | $message = 'Use of pre-decrement operator is disallowed.'; 59 | } 60 | } 61 | 62 | $phpcsFile->addError($message, $operatorPointer, $code); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Operators/SpreadOperatorSpacingSniff.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function register(): array 26 | { 27 | return [ 28 | T_ELLIPSIS, 29 | ]; 30 | } 31 | 32 | /** 33 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 34 | * @param int $spreadOperatorPointer 35 | */ 36 | public function process(File $phpcsFile, $spreadOperatorPointer): void 37 | { 38 | $this->spacesCountAfterOperator = SniffSettingsHelper::normalizeInteger($this->spacesCountAfterOperator); 39 | 40 | $pointerAfterWhitespace = TokenHelper::findNextNonWhitespace($phpcsFile, $spreadOperatorPointer + 1); 41 | 42 | $whitespace = TokenHelper::getContent($phpcsFile, $spreadOperatorPointer + 1, $pointerAfterWhitespace - 1); 43 | 44 | if ($this->spacesCountAfterOperator === strlen($whitespace)) { 45 | return; 46 | } 47 | 48 | $errorMessage = $this->spacesCountAfterOperator === 0 49 | ? 'There must be no whitespace after spread operator.' 50 | : sprintf( 51 | 'There must be exactly %d whitespace%s after spread operator.', 52 | $this->spacesCountAfterOperator, 53 | $this->spacesCountAfterOperator !== 1 ? 's' : '', 54 | ); 55 | 56 | $fix = $phpcsFile->addFixableError($errorMessage, $spreadOperatorPointer, self::CODE_INCORRECT_SPACES_AFTER_OPERATOR); 57 | 58 | if (!$fix) { 59 | return; 60 | } 61 | 62 | $phpcsFile->fixer->beginChangeset(); 63 | 64 | $phpcsFile->fixer->addContent($spreadOperatorPointer, str_repeat(' ', $this->spacesCountAfterOperator)); 65 | FixerHelper::removeBetween($phpcsFile, $spreadOperatorPointer, $pointerAfterWhitespace); 66 | 67 | $phpcsFile->fixer->endChangeset(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/PHP/DisallowDirectMagicInvokeCallSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_STRING, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $stringPointer 32 | */ 33 | public function process(File $phpcsFile, $stringPointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $stringPointer + 1); 38 | if ($tokens[$parenthesisOpenerPointer]['code'] !== T_OPEN_PARENTHESIS) { 39 | return; 40 | } 41 | 42 | if (strtolower($tokens[$stringPointer]['content']) !== '__invoke') { 43 | return; 44 | } 45 | 46 | $objectOperator = TokenHelper::findPreviousEffective($phpcsFile, $stringPointer - 1); 47 | if ($tokens[$objectOperator]['code'] !== T_OBJECT_OPERATOR) { 48 | return; 49 | } 50 | 51 | $fix = $phpcsFile->addFixableError( 52 | 'Direct call of __invoke() is disallowed.', 53 | $stringPointer, 54 | self::CODE_DISALLOWED_DIRECT_MAGIC_INVOKE_CALL, 55 | ); 56 | if (!$fix) { 57 | return; 58 | } 59 | 60 | $phpcsFile->fixer->beginChangeset(); 61 | 62 | FixerHelper::removeBetweenIncluding($phpcsFile, $objectOperator, $parenthesisOpenerPointer - 1); 63 | 64 | $phpcsFile->fixer->endChangeset(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/PHP/RequireNowdocSniff.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function register(): array 23 | { 24 | return [ 25 | T_START_HEREDOC, 26 | ]; 27 | } 28 | 29 | /** 30 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 31 | * @param int $heredocStartPointer 32 | */ 33 | public function process(File $phpcsFile, $heredocStartPointer): void 34 | { 35 | $tokens = $phpcsFile->getTokens(); 36 | 37 | $heredocEndPointer = TokenHelper::findNext($phpcsFile, T_END_HEREDOC, $heredocStartPointer + 1); 38 | 39 | $heredocContentPointers = []; 40 | for ($i = $heredocStartPointer + 1; $i < $heredocEndPointer; $i++) { 41 | if ($tokens[$i]['code'] === T_HEREDOC) { 42 | if (preg_match('~^([^\\\\$]|\\\\[^nrtvef0-7xu])*$~', $tokens[$i]['content']) === 0) { 43 | return; 44 | } 45 | 46 | $heredocContentPointers[] = $i; 47 | } 48 | } 49 | 50 | $fix = $phpcsFile->addFixableError('Use nowdoc syntax instead of heredoc.', $heredocStartPointer, self::CODE_REQUIRED_NOWDOC); 51 | if (!$fix) { 52 | return; 53 | } 54 | 55 | $nowdocStart = preg_replace('~^<<<"?(\w+)"?~', '<<<\'$1\'', $tokens[$heredocStartPointer]['content']); 56 | 57 | $phpcsFile->fixer->beginChangeset(); 58 | $phpcsFile->fixer->replaceToken($heredocStartPointer, $nowdocStart); 59 | 60 | foreach ($heredocContentPointers as $heredocContentPointer) { 61 | $heredocContent = $tokens[$heredocContentPointer]['content']; 62 | $nowdocContent = preg_replace( 63 | '~\\\\(\\\\[nrtvef]|\$|\\\\|\\\\[0-7]{1,3}|\\\\x[0-9A-Fa-f]{1,2}|\\\\u\{[0-9A-Fa-f]+\})~', 64 | '$1', 65 | $heredocContent, 66 | ); 67 | 68 | $phpcsFile->fixer->replaceToken($heredocContentPointer, $nowdocContent); 69 | } 70 | 71 | $phpcsFile->fixer->endChangeset(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/PHP/ShortListSniff.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function register(): array 21 | { 22 | return [T_LIST]; 23 | } 24 | 25 | /** 26 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 27 | * @param int $pointer 28 | */ 29 | public function process(File $phpcsFile, $pointer): void 30 | { 31 | $fix = $phpcsFile->addFixableError('list(...) is forbidden, use [...] instead.', $pointer, self::CODE_LONG_LIST_USED); 32 | 33 | if (!$fix) { 34 | return; 35 | } 36 | 37 | $tokens = $phpcsFile->getTokens(); 38 | 39 | /** @var int $startPointer */ 40 | $startPointer = TokenHelper::findNext($phpcsFile, [T_OPEN_PARENTHESIS], $pointer + 1); 41 | $endPointer = $tokens[$startPointer]['parenthesis_closer']; 42 | 43 | $phpcsFile->fixer->beginChangeset(); 44 | FixerHelper::removeBetweenIncluding($phpcsFile, $pointer, $startPointer - 1); 45 | $phpcsFile->fixer->replaceToken($startPointer, '['); 46 | $phpcsFile->fixer->replaceToken($endPointer, ']'); 47 | $phpcsFile->fixer->endChangeset(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/TypeHints/DisallowMixedTypeHintSniff.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function register(): array 31 | { 32 | return [ 33 | T_DOC_COMMENT_OPEN_TAG, 34 | ]; 35 | } 36 | 37 | /** 38 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 39 | * @param int $docCommentOpenPointer 40 | */ 41 | public function process(File $phpcsFile, $docCommentOpenPointer): void 42 | { 43 | if (SuppressHelper::isSniffSuppressed( 44 | $phpcsFile, 45 | $docCommentOpenPointer, 46 | $this->getSniffName(self::CODE_DISALLOWED_MIXED_TYPE_HINT), 47 | )) { 48 | return; 49 | } 50 | 51 | if ($this->targetHasOverrideAttribute($phpcsFile, $docCommentOpenPointer)) { 52 | return; 53 | } 54 | 55 | $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); 56 | 57 | foreach ($annotations as $annotation) { 58 | $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); 59 | 60 | foreach ($identifierTypeNodes as $typeHintNode) { 61 | $typeHint = $typeHintNode->name; 62 | 63 | if (strtolower($typeHint) !== 'mixed') { 64 | continue; 65 | } 66 | 67 | $phpcsFile->addError( 68 | 'Usage of "mixed" type hint is disallowed.', 69 | $annotation->getStartPointer(), 70 | self::CODE_DISALLOWED_MIXED_TYPE_HINT, 71 | ); 72 | } 73 | } 74 | } 75 | 76 | private function getSniffName(string $sniffName): string 77 | { 78 | return sprintf('%s.%s', self::NAME, $sniffName); 79 | } 80 | 81 | private function targetHasOverrideAttribute(File $phpcsFile, int $docCommentOpenPointer): bool 82 | { 83 | $tokens = $phpcsFile->getTokens(); 84 | $nextPointer = TokenHelper::findNextEffective($phpcsFile, $docCommentOpenPointer + 1); 85 | 86 | if ($nextPointer === null || $tokens[$nextPointer]['code'] !== T_ATTRIBUTE) { 87 | return false; 88 | } 89 | 90 | $attributeNames = array_map( 91 | static fn (Attribute $name): string => $name->getName(), 92 | AttributeHelper::getAttributes($phpcsFile, $nextPointer), 93 | ); 94 | 95 | return in_array('Override', $attributeNames, true) || in_array('\Override', $attributeNames, true); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/TypeHints/LongTypeHintsSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_DOC_COMMENT_OPEN_TAG, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $docCommentOpenPointer 33 | */ 34 | public function process(File $phpcsFile, $docCommentOpenPointer): void 35 | { 36 | $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer); 37 | 38 | foreach ($annotations as $annotation) { 39 | $identifierTypeNodes = AnnotationHelper::getAnnotationNodesByType($annotation->getNode(), IdentifierTypeNode::class); 40 | 41 | foreach ($identifierTypeNodes as $typeHintNode) { 42 | $typeHint = $typeHintNode->name; 43 | 44 | $lowercasedTypeHint = strtolower($typeHint); 45 | 46 | $shortTypeHint = null; 47 | if ($lowercasedTypeHint === 'integer') { 48 | $shortTypeHint = 'int'; 49 | } elseif ($lowercasedTypeHint === 'boolean') { 50 | $shortTypeHint = 'bool'; 51 | } 52 | 53 | if ($shortTypeHint === null) { 54 | continue; 55 | } 56 | 57 | $fix = $phpcsFile->addFixableError(sprintf( 58 | 'Expected "%s" but found "%s" in %s annotation.', 59 | $shortTypeHint, 60 | $typeHint, 61 | $annotation->getName(), 62 | ), $annotation->getStartPointer(), self::CODE_USED_LONG_TYPE_HINT); 63 | 64 | if (!$fix) { 65 | continue; 66 | } 67 | 68 | $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenPointer); 69 | 70 | $fixedDocComment = AnnotationHelper::fixAnnotation( 71 | $parsedDocComment, 72 | $annotation, 73 | $typeHintNode, 74 | new IdentifierTypeNode($shortTypeHint), 75 | ); 76 | 77 | $phpcsFile->fixer->beginChangeset(); 78 | 79 | FixerHelper::change( 80 | $phpcsFile, 81 | $parsedDocComment->getOpenPointer(), 82 | $parsedDocComment->getClosePointer(), 83 | $fixedDocComment, 84 | ); 85 | 86 | $phpcsFile->fixer->endChangeset(); 87 | } 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/TypeHints/UselessConstantTypeHintSniff.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function register(): array 25 | { 26 | return [ 27 | T_CONST, 28 | ]; 29 | } 30 | 31 | /** 32 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 33 | * @param int $constantPointer 34 | */ 35 | public function process(File $phpcsFile, $constantPointer): void 36 | { 37 | $tokens = $phpcsFile->getTokens(); 38 | 39 | $docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $constantPointer); 40 | if ($docCommentOpenPointer === null) { 41 | return; 42 | } 43 | 44 | $annotations = AnnotationHelper::getAnnotations($phpcsFile, $constantPointer, '@var'); 45 | 46 | if ($annotations === []) { 47 | return; 48 | } 49 | 50 | $uselessDocComment = !DocCommentHelper::hasDocCommentDescription($phpcsFile, $constantPointer) && count($annotations) === 1; 51 | if ($uselessDocComment) { 52 | $fix = $phpcsFile->addFixableError('Useless documentation comment.', $docCommentOpenPointer, self::CODE_USELESS_DOC_COMMENT); 53 | 54 | /** @var int $fixerStart */ 55 | $fixerStart = TokenHelper::findLastTokenOnPreviousLine($phpcsFile, $docCommentOpenPointer); 56 | $fixerEnd = $tokens[$docCommentOpenPointer]['comment_closer']; 57 | } else { 58 | $annotation = $annotations[0]; 59 | 60 | $fix = $phpcsFile->addFixableError( 61 | 'Useless @var annotation.', 62 | $annotation->getStartPointer(), 63 | self::CODE_USELESS_VAR_ANNOTATION, 64 | ); 65 | 66 | /** @var int $fixerStart */ 67 | $fixerStart = TokenHelper::findPreviousContent( 68 | $phpcsFile, 69 | T_DOC_COMMENT_WHITESPACE, 70 | $phpcsFile->eolChar, 71 | $annotation->getStartPointer() - 1, 72 | ); 73 | $fixerEnd = $annotation->getEndPointer(); 74 | } 75 | 76 | if (!$fix) { 77 | return; 78 | } 79 | 80 | $phpcsFile->fixer->beginChangeset(); 81 | FixerHelper::removeBetweenIncluding($phpcsFile, $fixerStart, $fixerEnd); 82 | $phpcsFile->fixer->endChangeset(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Variables/DisallowSuperGlobalVariableSniff.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function register(): array 31 | { 32 | return [ 33 | T_VARIABLE, 34 | ]; 35 | } 36 | 37 | /** 38 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 39 | * @param int $pointer 40 | */ 41 | public function process(File $phpcsFile, $pointer): void 42 | { 43 | $tokens = $phpcsFile->getTokens(); 44 | 45 | if (!in_array($tokens[$pointer]['content'], self::SUPER_GLOBALS, true)) { 46 | return; 47 | } 48 | 49 | $phpcsFile->addError('Use of super global variable is disallowed.', $pointer, self::CODE_DISALLOWED_SUPER_GLOBAL_VARIABLE); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Variables/DisallowVariableVariableSniff.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function register(): array 19 | { 20 | return [ 21 | T_DOLLAR, 22 | T_DOLLAR_OPEN_CURLY_BRACES, 23 | ]; 24 | } 25 | 26 | /** 27 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 28 | * @param int $pointer 29 | */ 30 | public function process(File $phpcsFile, $pointer): void 31 | { 32 | $phpcsFile->addError('Use of variable variable is disallowed.', $pointer, self::CODE_DISALLOWED_VARIABLE_VARIABLE); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/Sniffs/Variables/DuplicateAssignmentToVariableSniff.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function register(): array 24 | { 25 | return [ 26 | T_EQUAL, 27 | ]; 28 | } 29 | 30 | /** 31 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 32 | * @param int $assignmentPointer 33 | */ 34 | public function process(File $phpcsFile, $assignmentPointer): void 35 | { 36 | $tokens = $phpcsFile->getTokens(); 37 | 38 | $variablePointer = TokenHelper::findPreviousEffective($phpcsFile, $assignmentPointer - 1); 39 | if ($tokens[$variablePointer]['code'] !== T_VARIABLE) { 40 | return; 41 | } 42 | 43 | $pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1); 44 | if (in_array($tokens[$pointerBeforeVariable]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) { 45 | return; 46 | } 47 | 48 | /** @var int $secondVariablePointer */ 49 | $secondVariablePointer = TokenHelper::findNextEffective($phpcsFile, $assignmentPointer + 1); 50 | if ($tokens[$secondVariablePointer]['code'] !== T_VARIABLE) { 51 | return; 52 | } 53 | 54 | if ($tokens[$variablePointer]['content'] !== $tokens[$secondVariablePointer]['content']) { 55 | return; 56 | } 57 | 58 | $pointerAfterSecondVariable = TokenHelper::findNextEffective($phpcsFile, $secondVariablePointer + 1); 59 | if ($tokens[$pointerAfterSecondVariable]['code'] !== T_EQUAL) { 60 | return; 61 | } 62 | 63 | $phpcsFile->addError( 64 | sprintf('Duplicate assignment to variable %s.', $tokens[$secondVariablePointer]['content']), 65 | $secondVariablePointer, 66 | self::CODE_DUPLICATE_ASSIGNMENT, 67 | ); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /SlevomatCodingStandard/ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./../autoload-bootstrap.php 4 | 5 | -------------------------------------------------------------------------------- /autoload-bootstrap.php: -------------------------------------------------------------------------------- 1 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | #### SlevomatCodingStandard.Attributes.DisallowAttributesJoining 🔧 37 | 38 | Requires that only one attribute can be placed inside `#[]` (no comma-separated list). In case of more attributes applied, they are split into individual `#[]` blocks. 39 | 40 | #### SlevomatCodingStandard.Attributes.DisallowMultipleAttributesPerLine 🔧 41 | 42 | Disallows multiple attributes of some target on same line. 43 | This sniff treats multiple attributes declared inside one `#[]` as a single attribute. See `DisallowAttributesJoining` to modify this behavior. 44 | 45 | #### SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment 🔧 46 | 47 | Requires that attributes are always after documentation comment. 48 | 49 | -------------------------------------------------------------------------------- /doc/complexity.md: -------------------------------------------------------------------------------- 1 | ## Complexity 2 | 3 | #### SlevomatCodingStandard.Complexity.Cognitive 4 | 5 | Enforces maximum [cognitive complexity](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) for functions. 6 | 7 | Sniff provides the following setting: 8 | 9 | * `warningThreshold` (default: `6`) 10 | * `errorThreshold` (default: `6`) 11 | -------------------------------------------------------------------------------- /doc/exceptions.md: -------------------------------------------------------------------------------- 1 | ## Exceptions 2 | 3 | #### SlevomatCodingStandard.Exceptions.DeadCatch 4 | 5 | This sniff finds unreachable catch blocks: 6 | 7 | ```php 8 | try { 9 | doStuff(); 10 | } catch (\Throwable $e) { 11 | log($e); 12 | } catch (\InvalidArgumentException $e) { 13 | // unreachable! 14 | } 15 | ``` 16 | 17 | #### SlevomatCodingStandard.Exceptions.DisallowNonCapturingCatch 18 | 19 | This sniff forbids use of non-capturing catch introduced in PHP 8.0 [PHP RFC: non-capturing catches](https://wiki.php.net/rfc/non-capturing_catches). 20 | 21 | #### SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly 🔧🚧 22 | 23 | In PHP 7.0, a [`Throwable` interface was added](https://wiki.php.net/rfc/throwable-interface) that allows catching and handling errors in more cases than `Exception` previously allowed. So, if the catch statement contained `Exception` on PHP 5.x, it means it should probably be rewritten to reference `Throwable` on PHP 7.x. This sniff enforces that. 24 | 25 | #### SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch 🔧 26 | 27 | Sniff provides the following settings: 28 | 29 | * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher. 30 | 31 | It requires non-capturing catch when the variable with exception is not used. 32 | -------------------------------------------------------------------------------- /doc/files.md: -------------------------------------------------------------------------------- 1 | ## Files 2 | 3 | #### SlevomatCodingStandard.Files.FileLength 4 | 5 | Disallows long files. This sniff provides the following settings: 6 | 7 | * `includeComments` (default: `false`): should comments be included in the count. 8 | * `includeWhitespace` (default: `false`): should empty lines be included in the count. 9 | * `maxLinesLength` (default: `250`): specifies max allowed function lines length. 10 | 11 | #### SlevomatCodingStandard.Files.LineLength 12 | 13 | Enforces maximum length of a single line of code. 14 | 15 | Sniff provides the following settings: 16 | 17 | * `lineLengthLimit`: actual limit of the line length 18 | * `ignoreComments`: whether to ignore line length of comments 19 | * `ignoreImports`: whether to ignore line length of import (use) statements 20 | 21 | #### SlevomatCodingStandard.Files.TypeNameMatchesFileName 22 | 23 | For projects not following the [PSR-0](http://www.php-fig.org/psr/psr-0/) or [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloading standards, this sniff checks whether a namespace and a name of a class/interface/trait follows agreed-on way to organize code into directories and files. 24 | 25 | Other than enforcing that the type name must match the name of the file it's contained in, this sniff is very configurable. Consider the following sample configuration: 26 | 27 | ```xml 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ``` 53 | 54 | Sniff provides the following settings: 55 | 56 | * `rootNamespaces` property expects configuration similar to PSR-4 - project directories mapped to certain namespaces. 57 | * `skipDirs` are not taken into consideration when comparing a path to a namespace. For example, with the above settings, file at path `app/services/Product/Product.php` is expected to contain `Slevomat\Product\Product`, not `Slevomat\services\Product\Product`. 58 | * `extensions`: allow different file extensions. Default is `php`. 59 | * `ignoredNamespaces`: sniff is not performed on these namespaces. 60 | -------------------------------------------------------------------------------- /doc/numbers.md: -------------------------------------------------------------------------------- 1 | ## Numbers 2 | 3 | #### SlevomatCodingStandard.Numbers.DisallowNumericLiteralSeparator 🔧 4 | 5 | Disallows numeric literal separators. 6 | 7 | #### SlevomatCodingStandard.Numbers.RequireNumericLiteralSeparator 8 | 9 | Requires use of numeric literal separators. 10 | 11 | This sniff provides the following setting: 12 | 13 | * `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 7.4 or higher. 14 | * `minDigitsBeforeDecimalPoint`: the minimum digits before decimal point to require separator. 15 | * `minDigitsAfterDecimalPoint`: the minimum digits after decimal point to require separator. 16 | * `ignoreOctalNumbers`: to ignore octal numbers. 17 | -------------------------------------------------------------------------------- /doc/operators.md: -------------------------------------------------------------------------------- 1 | ## Operators 2 | 3 | #### SlevomatCodingStandard.Operators.DisallowEqualOperators 🔧 4 | 5 | Disallows using loose `==` and `!=` comparison operators. Use `===` and `!==` instead, they are much more secure and predictable. 6 | 7 | #### SlevomatCodingStandard.Operators.DisallowIncrementAndDecrementOperators 8 | 9 | Disallows using `++` and `--` operators. 10 | 11 | #### SlevomatCodingStandard.Operators.NegationOperatorSpacing 🔧 12 | 13 | Checks if there is the same number of spaces after negation operator as expected. 14 | 15 | Sniff provides the following settings: 16 | 17 | * `spacesCount`: the number of spaces expected after the negation operator 18 | 19 | #### SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator 🔧 20 | 21 | Requires using combined assignment operators, eg `+=`, `.=` etc. 22 | 23 | #### SlevomatCodingStandard.Operators.RequireOnlyStandaloneIncrementAndDecrementOperators 24 | 25 | Reports `++` and `--` operators not used standalone. 26 | 27 | #### SlevomatCodingStandard.Operators.SpreadOperatorSpacing 🔧 28 | 29 | Enforces configurable number of spaces after the `...` operator. 30 | 31 | Sniff provides the following settings: 32 | 33 | * `spacesCountAfterOperator`: the number of spaces after the `...` operator. 34 | -------------------------------------------------------------------------------- /doc/strings.md: -------------------------------------------------------------------------------- 1 | ## Strings 2 | 3 | #### SlevomatCodingStandard.Strings.DisallowVariableParsing 4 | 5 | Disallows variable parsing inside strings. 6 | 7 | Sniff provides the following settings: 8 | 9 | * `disallowDollarCurlySyntax`: disallows usage of `${...}`, enabled by default. 10 | * `disallowCurlyDollarSyntax`: disallows usage of `{$...}`, disabled by default. 11 | * `disallowSimpleSyntax`: disallows usage of `$...`, disabled by default. 12 | -------------------------------------------------------------------------------- /doc/variables.md: -------------------------------------------------------------------------------- 1 | ## Variables 2 | 3 | #### SlevomatCodingStandard.Variables.DisallowSuperGlobalVariable 4 | 5 | Disallows use of super global variables. 6 | 7 | #### SlevomatCodingStandard.Variables.DisallowVariableVariable 8 | 9 | Disallows use of variable variables. 10 | 11 | #### SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable 12 | 13 | Looks for duplicate assignments to a variable. 14 | 15 | #### SlevomatCodingStandard.Variables.UnusedVariable 16 | 17 | Looks for unused variables. 18 | 19 | Sniff provides the following settings: 20 | 21 | * `ignoreUnusedValuesWhenOnlyKeysAreUsedInForeach` (default: `false`): ignore unused `$value` in foreach when only `$key` is used 22 | 23 | ```php 24 | foreach ($values as $key => $value) { 25 | echo $key; 26 | } 27 | ``` 28 | 29 | #### SlevomatCodingStandard.Variables.UselessVariable 🔧 30 | 31 | Looks for useless variables. 32 | -------------------------------------------------------------------------------- /doc/whitespaces.md: -------------------------------------------------------------------------------- 1 | ## Whitespaces 2 | 3 | #### SlevomatCodingStandard.Whitespaces.DuplicateSpaces 🔧 4 | 5 | Checks duplicate spaces anywhere because there aren't sniffs for every part of code to check formatting. 6 | 7 | Sniff provides the following settings: 8 | 9 | * `ignoreSpacesBeforeAssignment`: to allow multiple spaces to align assignments. 10 | * `ignoreSpacesInAnnotation`: to allow multiple spaces to align annotations. 11 | * `ignoreSpacesInComment`: to allow multiple spaces to align content of the comment. 12 | * `ignoreSpacesInParameters`: to allow multiple spaces to align parameters. 13 | * `ignoreSpacesInMatch`: to allow multiple spaces to align `match` expressions. 14 | --------------------------------------------------------------------------------