diff --git a/README.md b/README.md index f140f050..d5b69afc 100644 --- a/README.md +++ b/README.md @@ -16,84 +16,9 @@ composer require symplify/phpstan-rules --dev
-## A. Add Prepared Sets +## Register Rules you Need -Sets are bunch of rules grouped by a common area, e.g. improve naming. You can pick from 5 sets: - -```yaml -includes: - - vendor/symplify/phpstan-rules/config/code-complexity-rules.neon - - vendor/symplify/phpstan-rules/config/naming-rules.neon - - vendor/symplify/phpstan-rules/config/regex-rules.neon - - vendor/symplify/phpstan-rules/config/static-rules.neon -``` - -
- -## B. Cherry-pick Configurable Rules - -There is one set with pre-configured configurable rules. Include it and see what is errors are found: - -```yaml -# phpstan.neon -includes: - - vendor/symplify/phpstan-rules/config/configurable-rules.neon -``` - -
- -Would you like to tailor it to fit your taste? Pick one PHPStan rule and configure it manually ↓ - -```yaml -services: - - - class: Symplify\PHPStanRules\Rules\ForbiddenNodeRule - tags: [phpstan.rules.rule] - arguments: - forbiddenNodes: - - PhpParser\Node\Expr\Empty_ - - PhpParser\Node\Stmt\Switch_ -``` - -
- -## 3. Register Particular Rules - -
- -# 30 Rules Overview - -## AnnotateRegexClassConstWithRegexLinkRule - -Add regex101.com link to that shows the regex in practise, so it will be easier to maintain in case of bug/extension in the future - -```yaml -rules: - - Symplify\PHPStanRules\Rules\AnnotateRegexClassConstWithRegexLinkRule -``` - -```php -class SomeClass -{ - private const COMPLICATED_REGEX = '#some_complicated_stu|ff#'; -} -``` - -:x: - -
- -```php -class SomeClass -{ - /** - * @see https://regex101.com/r/SZr0X5/12 - */ - private const COMPLICATED_REGEX = '#some_complicated_stu|ff#'; -} -``` - -:+1: +Pick from 25+ rules:
@@ -562,48 +487,6 @@ class SomeClass
-## NoInlineStringRegexRule - -Use local named constant instead of inline string for regex to explain meaning by constant name - -```yaml -rules: - - Symplify\PHPStanRules\Rules\NoInlineStringRegexRule -``` - -```php -class SomeClass -{ - public function run($value) - { - return preg_match('#some_stu|ff#', $value); - } -} -``` - -:x: - -
- -```php -class SomeClass -{ - /** - * @var string - */ - public const SOME_STUFF_REGEX = '#some_stu|ff#'; - - public function run($value) - { - return preg_match(self::SOME_STUFF_REGEX, $value); - } -} -``` - -:+1: - -
- ## NoReferenceRule Use explicit return value over magic &reference @@ -895,47 +778,6 @@ class SomeClass extends SomeParentClass
-## RegexSuffixInRegexConstantRule - -Name your constant with "_REGEX" suffix, instead of "%s" - -```yaml -rules: - - Symplify\PHPStanRules\Rules\RegexSuffixInRegexConstantRule -``` - -```php -class SomeClass -{ - public const SOME_NAME = '#some\s+name#'; - - public function run($value) - { - $somePath = preg_match(self::SOME_NAME, $value); - } -} -``` - -:x: - -
- -```php -class SomeClass -{ - public const SOME_NAME_REGEX = '#some\s+name#'; - - public function run($value) - { - $somePath = preg_match(self::SOME_NAME_REGEX, $value); - } -} -``` - -:+1: - -
- ## RequireAttributeNameRule Attribute must have all names explicitly defined diff --git a/config/regex-rules.neon b/config/regex-rules.neon deleted file mode 100644 index ec0420d3..00000000 --- a/config/regex-rules.neon +++ /dev/null @@ -1,4 +0,0 @@ -rules: - - Symplify\PHPStanRules\Rules\AnnotateRegexClassConstWithRegexLinkRule - - Symplify\PHPStanRules\Rules\NoInlineStringRegexRule - - Symplify\PHPStanRules\Rules\RegexSuffixInRegexConstantRule diff --git a/config/services/services.neon b/config/services/services.neon index 2395cf42..bf0f9853 100644 --- a/config/services/services.neon +++ b/config/services/services.neon @@ -9,8 +9,6 @@ services: - Symplify\PHPStanRules\Naming\ClassToSuffixResolver - Symplify\PHPStanRules\NodeAnalyzer\AttributeFinder - Symplify\PHPStanRules\NodeAnalyzer\EnumAnalyzer - - Symplify\PHPStanRules\NodeAnalyzer\RegexFuncCallAnalyzer - - Symplify\PHPStanRules\NodeAnalyzer\RegexStaticCallAnalyzer - Symplify\PHPStanRules\ParentClassMethodNodeResolver - Symplify\PHPStanRules\PhpDoc\BarePhpDocParser - Symplify\PHPStanRules\PhpDoc\PhpDocResolver diff --git a/config/symplify-rules.neon b/config/symplify-rules.neon index 2c96c831..c4a05d0e 100644 --- a/config/symplify-rules.neon +++ b/config/symplify-rules.neon @@ -2,5 +2,4 @@ includes: - code-complexity-rules.neon - configurable-rules.neon - naming-rules.neon - - regex-rules.neon - static-rules.neon diff --git a/src/Enum/ClassName.php b/src/Enum/ClassName.php index 19171193..ef02ba67 100644 --- a/src/Enum/ClassName.php +++ b/src/Enum/ClassName.php @@ -11,11 +11,6 @@ final class ClassName */ public const ROUTE_ATTRIBUTE = 'Symfony\Component\Routing\Annotation\Route'; - /** - * @var string - */ - public const NETTE_STRINGS = 'Nette\Utils\Strings'; - /** * @var string */ diff --git a/src/Enum/RuleIdentifier.php b/src/Enum/RuleIdentifier.php index 0d6ce3e5..9f84e80e 100644 --- a/src/Enum/RuleIdentifier.php +++ b/src/Enum/RuleIdentifier.php @@ -16,11 +16,6 @@ final class RuleIdentifier */ public const SEE_ANNOTATION_TO_TEST = 'symplify.seeAnnotationToTest'; - /** - * @var string - */ - public const REGEX_SUFFIX_IN_REGEX_CONSTANT = 'symplify.regexSuffixInRegexConstant'; - /** * @var string */ @@ -66,11 +61,6 @@ final class RuleIdentifier */ public const PARENT_METHOD_VISIBILITY_OVERRIDE = 'symplify.parentMethodVisibilityOverride'; - /** - * @var string - */ - public const CLASS_CONSTANT_REGEX = 'symplify.classConstantRegex'; - /** * @var string */ @@ -126,11 +116,6 @@ final class RuleIdentifier */ public const FORBIDDEN_ARRAY_METHOD_CALL = 'symplify.forbiddenArrayMethodCall'; - /** - * @var string - */ - public const REGEX_ANNOTATE_CLASS_CONST = 'symplify.regexAnnotateClassConst'; - /** * @var string */ diff --git a/src/NodeAnalyzer/RegexFuncCallAnalyzer.php b/src/NodeAnalyzer/RegexFuncCallAnalyzer.php deleted file mode 100644 index 29c10f60..00000000 --- a/src/NodeAnalyzer/RegexFuncCallAnalyzer.php +++ /dev/null @@ -1,32 +0,0 @@ -name instanceof Name) { - return false; - } - - $funcCallName = $funcCall->name->toString(); - return in_array($funcCallName, self::FUNC_CALLS_WITH_FIRST_ARG_REGEX, true); - } -} diff --git a/src/NodeAnalyzer/RegexStaticCallAnalyzer.php b/src/NodeAnalyzer/RegexStaticCallAnalyzer.php deleted file mode 100644 index 0986cf1c..00000000 --- a/src/NodeAnalyzer/RegexStaticCallAnalyzer.php +++ /dev/null @@ -1,36 +0,0 @@ -class instanceof Name) { - return false; - } - - if ($staticCall->class->toString() !== ClassName::NETTE_STRINGS) { - return false; - } - - if (! $staticCall->name instanceof Identifier) { - return false; - } - - $staticCallName = $staticCall->name->toString(); - return in_array($staticCallName, self::NETTE_UTILS_CALLS_METHOD_NAMES_WITH_SECOND_ARG_REGEX, true); - } -} diff --git a/src/Rules/AnnotateRegexClassConstWithRegexLinkRule.php b/src/Rules/AnnotateRegexClassConstWithRegexLinkRule.php deleted file mode 100644 index e06c70c3..00000000 --- a/src/Rules/AnnotateRegexClassConstWithRegexLinkRule.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @see \Symplify\PHPStanRules\Tests\Rules\AnnotateRegexClassConstWithRegexLinkRule\AnnotateRegexClassConstWithRegexLinkRuleTest - */ -final class AnnotateRegexClassConstWithRegexLinkRule implements Rule -{ - /** - * @var string - */ - public const ERROR_MESSAGE = 'Add regex101.com link to that shows the regex in practise, so it will be easier to maintain in case of bug/extension in the future'; - - /** - * @var string - * @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php - */ - private const ALL_MODIFIERS = 'imsxeADSUXJu'; - - public function getNodeType(): string - { - return ClassConst::class; - } - - /** - * @param ClassConst $node - */ - public function processNode(Node $node, Scope $scope): array - { - if (count($node->consts) !== 1) { - return []; - } - - $onlyConst = $node->consts[0]; - if (! $onlyConst->value instanceof String_) { - return []; - } - - $constantName = (string) $onlyConst->name; - if (! $this->isRegexPatternConstantName($constantName)) { - return []; - } - - $stringValue = $onlyConst->value->value; - if (! $this->isNonSingleCharRegexPattern($stringValue)) { - return []; - } - - // is regex patern - if ($this->hasDocBlockWithRegexLink($node)) { - return []; - } - - return [RuleErrorBuilder::message(self::ERROR_MESSAGE) - ->identifier(RuleIdentifier::REGEX_ANNOTATE_CLASS_CONST) - ->build()]; - } - - private function isNonSingleCharRegexPattern(string $value): bool - { - // skip 1-char regexs - if (strlen($value) < 4) { - return false; - } - - $firstChar = $value[0]; - - if (ctype_alpha($firstChar)) { - return false; - } - - $patternWithoutModifiers = rtrim($value, self::ALL_MODIFIERS); - - if (strlen($patternWithoutModifiers) < 1) { - return false; - } - - $lastChar = substr($patternWithoutModifiers, -1, 1); - - // this is probably a regex - return $firstChar === $lastChar; - } - - private function hasDocBlockWithRegexLink(ClassConst $classConst): bool - { - $docComment = $classConst->getDocComment(); - if (! $docComment instanceof Doc) { - return false; - } - - $docCommentText = $docComment->getText(); - return \str_contains($docCommentText, '@see https://regex101.com/r'); - } - - private function isRegexPatternConstantName(string $constantName): bool - { - return \str_ends_with($constantName, '_REGEX'); - } -} diff --git a/src/Rules/NoInlineStringRegexRule.php b/src/Rules/NoInlineStringRegexRule.php deleted file mode 100644 index 09ec5485..00000000 --- a/src/Rules/NoInlineStringRegexRule.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @see \Symplify\PHPStanRules\Tests\Rules\NoInlineStringRegexRule\NoInlineStringRegexRuleTest - */ -final readonly class NoInlineStringRegexRule implements Rule -{ - /** - * @var string - */ - public const ERROR_MESSAGE = 'Use local named constant instead of inline string for regex to explain meaning by constant name'; - - public function __construct( - private RegexFuncCallAnalyzer $regexFuncCallAnalyzer, - private RegexStaticCallAnalyzer $regexStaticCallAnalyzer - ) { - } - - public function getNodeType(): string - { - return CallLike::class; - } - - /** - * @param CallLike $node - */ - public function processNode(Node $node, Scope $scope): array - { - if ($node instanceof FuncCall) { - return $this->processRegexFuncCall($node); - } - - if ($node instanceof StaticCall) { - return $this->processRegexStaticCall($node); - } - - return []; - } - - /** - * @return list - */ - private function processRegexFuncCall(FuncCall $funcCall): array - { - if (! $this->regexFuncCallAnalyzer->isRegexFuncCall($funcCall)) { - return []; - } - - $firstArg = $funcCall->getArgs()[0]; - - // it's not string → good - if (! $firstArg->value instanceof String_) { - return []; - } - - return [RuleErrorBuilder::message(self::ERROR_MESSAGE) - ->identifier(RuleIdentifier::CLASS_CONSTANT_REGEX) - ->build()]; - } - - /** - * @return list - */ - private function processRegexStaticCall(StaticCall $staticCall): array - { - if (! $this->regexStaticCallAnalyzer->isRegexStaticCall($staticCall)) { - return []; - } - - $secondArg = $staticCall->getArgs()[1]; - $secondArgValue = $secondArg->value; - - // it's not string → good - if (! $secondArgValue instanceof String_) { - return []; - } - - $regexValue = $secondArgValue->value; - - if (strlen($regexValue) <= 7) { - return []; - } - - return [RuleErrorBuilder::message(self::ERROR_MESSAGE) - ->identifier(RuleIdentifier::CLASS_CONSTANT_REGEX) - ->build()]; - } -} diff --git a/src/Rules/RegexSuffixInRegexConstantRule.php b/src/Rules/RegexSuffixInRegexConstantRule.php deleted file mode 100644 index 37cf9d7e..00000000 --- a/src/Rules/RegexSuffixInRegexConstantRule.php +++ /dev/null @@ -1,108 +0,0 @@ - - * @see \Symplify\PHPStanRules\Tests\Rules\RegexSuffixInRegexConstantRule\RegexSuffixInRegexConstantRuleTest - */ -final readonly class RegexSuffixInRegexConstantRule implements Rule -{ - /** - * @var string - */ - public const ERROR_MESSAGE = 'Name your constant with "_REGEX" suffix, instead of "%s"'; - - public function __construct( - private RegexFuncCallAnalyzer $regexFuncCallAnalyzer, - private RegexStaticCallAnalyzer $regexStaticCallAnalyzer - ) { - } - - public function getNodeType(): string - { - return CallLike::class; - } - - /** - * @param CallLike $node - */ - public function processNode(Node $node, Scope $scope): array - { - if ($node instanceof FuncCall) { - return $this->processFuncCall($node); - } - - if ($node instanceof StaticCall) { - return $this->processStaticCall($node); - } - - return []; - } - - /** - * @return list - */ - private function processConstantName(Expr $expr): array - { - if (! $expr instanceof ClassConstFetch) { - return []; - } - - if ($expr->name instanceof Expr) { - return []; - } - - $constantName = (string) $expr->name; - if (\str_ends_with($constantName, '_REGEX')) { - return []; - } - - $errorMessage = sprintf(self::ERROR_MESSAGE, $constantName); - return [RuleErrorBuilder::message($errorMessage) - ->identifier(RuleIdentifier::REGEX_SUFFIX_IN_REGEX_CONSTANT) - ->build()]; - } - - /** - * @return list - */ - private function processStaticCall(StaticCall $staticCall): array - { - if (! $this->regexStaticCallAnalyzer->isRegexStaticCall($staticCall)) { - return []; - } - - $secondArg = $staticCall->getArgs()[1]; - return $this->processConstantName($secondArg->value); - } - - /** - * @return list - */ - private function processFuncCall(FuncCall $funcCall): array - { - if (! $this->regexFuncCallAnalyzer->isRegexFuncCall($funcCall)) { - return []; - } - - $firstArg = $funcCall->getArgs()[0]; - return $this->processConstantName($firstArg->value); - } -} diff --git a/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/AnnotateRegexClassConstWithRegexLinkRuleTest.php b/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/AnnotateRegexClassConstWithRegexLinkRuleTest.php deleted file mode 100644 index 77c5e7a5..00000000 --- a/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/AnnotateRegexClassConstWithRegexLinkRuleTest.php +++ /dev/null @@ -1,49 +0,0 @@ -analyse([$filePath], $expectedErrorMessagesWithLines); - } - - public static function provideData(): Iterator - { - yield [ - __DIR__ . '/Fixture/ClassConstMissingLink.php', - [[AnnotateRegexClassConstWithRegexLinkRule::ERROR_MESSAGE, 12]], - ]; - - yield [__DIR__ . '/Fixture/SkipShort.php', []]; - yield [__DIR__ . '/Fixture/SkipWithLink.php', []]; - yield [__DIR__ . '/Fixture/SkipAlphabet.php', []]; - yield [__DIR__ . '/Fixture/SkipPlaceholder.php', []]; - } - - /** - * @return string[] - */ - public static function getAdditionalConfigFiles(): array - { - return [__DIR__ . '/config/configured_rule.neon']; - } - - protected function getRule(): Rule - { - return self::getContainer()->getByType(AnnotateRegexClassConstWithRegexLinkRule::class); - } -} diff --git a/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/Fixture/ClassConstMissingLink.php b/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/Fixture/ClassConstMissingLink.php deleted file mode 100644 index 4756d3b7..00000000 --- a/tests/Rules/AnnotateRegexClassConstWithRegexLinkRule/Fixture/ClassConstMissingLink.php +++ /dev/null @@ -1,13 +0,0 @@ -analyse([$filePath], $expectedErrorMessagesWithLines); - } - - public static function provideData(): Iterator - { - yield [__DIR__ . '/Fixture/InlineMatchRegex.php', [[NoInlineStringRegexRule::ERROR_MESSAGE, 11]]]; - yield [ - __DIR__ . '/Fixture/NetteUtilsStringsInlineMatchRegex.php', - [[NoInlineStringRegexRule::ERROR_MESSAGE, 13]], - ]; - - yield [__DIR__ . '/Fixture/SkipVariable.php', []]; - yield [__DIR__ . '/Fixture/SkipSingleLetter.php', []]; - yield [__DIR__ . '/Fixture/SkipConstRegex.php', []]; - yield [__DIR__ . '/Fixture/SkipNetteUtilsStringsConstRegex.php', []]; - } - - /** - * @return string[] - */ - public static function getAdditionalConfigFiles(): array - { - return [__DIR__ . '/config/configured_rule.neon']; - } - - protected function getRule(): Rule - { - return self::getContainer()->getByType(NoInlineStringRegexRule::class); - } -} diff --git a/tests/Rules/NoInlineStringRegexRule/config/configured_rule.neon b/tests/Rules/NoInlineStringRegexRule/config/configured_rule.neon deleted file mode 100644 index 06c63f38..00000000 --- a/tests/Rules/NoInlineStringRegexRule/config/configured_rule.neon +++ /dev/null @@ -1,5 +0,0 @@ -includes: - - ../../../config/included_services.neon - -rules: - - Symplify\PHPStanRules\Rules\NoInlineStringRegexRule diff --git a/tests/Rules/RegexSuffixInRegexConstantRule/Fixture/DifferentSuffix.php b/tests/Rules/RegexSuffixInRegexConstantRule/Fixture/DifferentSuffix.php deleted file mode 100644 index 5b297416..00000000 --- a/tests/Rules/RegexSuffixInRegexConstantRule/Fixture/DifferentSuffix.php +++ /dev/null @@ -1,17 +0,0 @@ -analyse([$filePath], $expectedErrorMessagesWithLines); - } - - public static function provideData(): Iterator - { - $errorMessage = sprintf(RegexSuffixInRegexConstantRule::ERROR_MESSAGE, 'SOME_NAME'); - - yield [__DIR__ . '/Fixture/DifferentSuffix.php', [[$errorMessage, 15]]]; - } - - /** - * @return string[] - */ - public static function getAdditionalConfigFiles(): array - { - return [__DIR__ . '/config/configured_rule.neon']; - } - - protected function getRule(): Rule - { - return self::getContainer()->getByType(RegexSuffixInRegexConstantRule::class); - } -} diff --git a/tests/Rules/RegexSuffixInRegexConstantRule/config/configured_rule.neon b/tests/Rules/RegexSuffixInRegexConstantRule/config/configured_rule.neon deleted file mode 100644 index 472dba51..00000000 --- a/tests/Rules/RegexSuffixInRegexConstantRule/config/configured_rule.neon +++ /dev/null @@ -1,5 +0,0 @@ -includes: - - ../../../config/included_services.neon - -rules: - - Symplify\PHPStanRules\Rules\RegexSuffixInRegexConstantRule