From fbe129022dc92d8482ec16f672b1e8dcba3d2789 Mon Sep 17 00:00:00 2001 From: LuigiCardamone Date: Fri, 20 Jan 2023 18:50:57 +0100 Subject: [PATCH] Add a configuration to skip DocBlock Custom Annotations detection (#336) (#337) --- README.md | 5 +++++ src/Analyzer/FileParserFactory.php | 4 ++-- src/Analyzer/NameResolver.php | 7 +++++- src/CLI/Config.php | 15 +++++++++++++ src/CLI/Runner.php | 2 +- tests/Unit/Analyzer/FileVisitorTest.php | 30 +++++++++++++++++++++++++ tests/Unit/CLI/ConfigTest.php | 9 ++++++++ 7 files changed, 68 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 786b031b..362b86a1 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,11 @@ return static function (Config $config): void { ->add($mvcClassSet, ...$rules); }; ``` +PHPArkitect can detect violations also on DocBlocks custom annotations (like `@Assert\NotBlank` or `@Serializer\Expose`). +If you want to disable this feature you can add this simple configuration: +```php +$config->skipParsingCustomAnnotations(); +``` # Available rules diff --git a/src/Analyzer/FileParserFactory.php b/src/Analyzer/FileParserFactory.php index 73f90e69..6807c34d 100644 --- a/src/Analyzer/FileParserFactory.php +++ b/src/Analyzer/FileParserFactory.php @@ -9,12 +9,12 @@ class FileParserFactory { - public static function createFileParser(TargetPhpVersion $targetPhpVersion): FileParser + public static function createFileParser(TargetPhpVersion $targetPhpVersion, bool $parseCustomAnnotations = true): FileParser { return new FileParser( new NodeTraverser(), new FileVisitor(ClassDescriptionBuilder::create()), - new NameResolver(), + new NameResolver(null, ['parseCustomAnnotations' => $parseCustomAnnotations]), $targetPhpVersion ); } diff --git a/src/Analyzer/NameResolver.php b/src/Analyzer/NameResolver.php index af5fff73..7669b60c 100644 --- a/src/Analyzer/NameResolver.php +++ b/src/Analyzer/NameResolver.php @@ -31,6 +31,9 @@ class NameResolver extends NodeVisitorAbstract /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */ protected $replaceNodes; + /** @var bool Whether to parse DocBlock Custom Annotations */ + protected $parseCustomAnnotations; + /** * Constructs a name resolution visitor. * @@ -40,6 +43,7 @@ class NameResolver extends NodeVisitorAbstract * * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a * resolvedName attribute is added. (Names that cannot be statically resolved receive a * namespacedName attribute, as usual.) + * * parseCustomAnnotations (default true): Whether to parse DocBlock Custom Annotations. * * @param ErrorHandler|null $errorHandler Error handler * @param array $options Options @@ -49,6 +53,7 @@ public function __construct(ErrorHandler $errorHandler = null, array $options = $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; $this->replaceNodes = $options['replaceNodes'] ?? true; + $this->parseCustomAnnotations = $options['parseCustomAnnotations'] ?? true; } /** @@ -148,7 +153,7 @@ public function enterNode(Node $node) break; } - if (!($node->type instanceof FullyQualified)) { + if ($this->parseCustomAnnotations && !($node->type instanceof FullyQualified)) { foreach ($phpDocNode->getTags() as $tagValue) { if ('@' === $tagValue->name[0] && false === strpos($tagValue->name, '@var')) { $customTag = str_replace('@', '', $tagValue->name); diff --git a/src/CLI/Config.php b/src/CLI/Config.php index 2ccd1809..039563f6 100644 --- a/src/CLI/Config.php +++ b/src/CLI/Config.php @@ -13,11 +13,14 @@ class Config private $classSetRules; /** @var bool */ private $runOnlyARule; + /** @var bool */ + private $parseCustomAnnotations; public function __construct() { $this->classSetRules = []; $this->runOnlyARule = false; + $this->parseCustomAnnotations = true; } public function add(ClassSet $classSet, ArchRule ...$rules): self @@ -46,4 +49,16 @@ public function getClassSetRules(): array { return $this->classSetRules; } + + public function skipParsingCustomAnnotations(): self + { + $this->parseCustomAnnotations = false; + + return $this; + } + + public function isParseCustomAnnotationsEnabled(): bool + { + return $this->parseCustomAnnotations; + } } diff --git a/src/CLI/Runner.php b/src/CLI/Runner.php index d6057441..7987c138 100644 --- a/src/CLI/Runner.php +++ b/src/CLI/Runner.php @@ -31,7 +31,7 @@ public function __construct(bool $stopOnFailure = false) public function run(Config $config, Progress $progress, TargetPhpVersion $targetPhpVersion): void { /** @var FileParser $fileParser */ - $fileParser = FileParserFactory::createFileParser($targetPhpVersion); + $fileParser = FileParserFactory::createFileParser($targetPhpVersion, $config->isParseCustomAnnotationsEnabled()); /** @var ClassSetRules $classSetRule */ foreach ($config->getClassSetRules() as $classSetRule) { diff --git a/tests/Unit/Analyzer/FileVisitorTest.php b/tests/Unit/Analyzer/FileVisitorTest.php index 668d5f73..a2a9f420 100644 --- a/tests/Unit/Analyzer/FileVisitorTest.php +++ b/tests/Unit/Analyzer/FileVisitorTest.php @@ -767,4 +767,34 @@ public function getRequest(): Request //the violations is reported here $this->assertCount(0, $violations); } + + public function test_it_skip_custom_annotations_in_docblocks_if_the_option_parse_custom_annotation_is_false(): void + { + $code = <<< 'EOF' +parse($code, 'relativePathName'); + + $cd = $fp->getClassDescriptions(); + + $violations = new Violations(); + + $dependsOnlyOnTheseNamespaces = new DependsOnlyOnTheseNamespaces('MyProject\AppBundle\Application'); + $dependsOnlyOnTheseNamespaces->evaluate($cd[0], $violations, 'we want to add this rule for our software'); + + $this->assertCount(0, $violations); + } } diff --git a/tests/Unit/CLI/ConfigTest.php b/tests/Unit/CLI/ConfigTest.php index bf26d177..93887379 100644 --- a/tests/Unit/CLI/ConfigTest.php +++ b/tests/Unit/CLI/ConfigTest.php @@ -55,4 +55,13 @@ public function test_it_should_create_config_with_only_one_rule_to_run(): void $classSetRulesExpected[] = ClassSetRules::create($classSet, ...[$rule2]); $this->assertEquals($classSetRulesExpected, $config->getClassSetRules()); } + + public function test_it_should_allow_to_change_the_default_value_for_parsing_custom_annotations(): void + { + $config = new Config(); + $this->assertTrue($config->isParseCustomAnnotationsEnabled()); + + $config->skipParsingCustomAnnotations(); + $this->assertFalse($config->isParseCustomAnnotationsEnabled()); + } }