diff --git a/README.md b/README.md index a971d46b..03eec96d 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ $rules[] = Rule::allClasses() ```php $rules[] = Rule::allClasses() ->that(new ResideInOneOfTheseNamespaces('App\Domain')) - ->should(new NotHaveDependencyOutsideNamespace('App\Domain', ['Ramsey\Uuid'])) + ->should(new NotHaveDependencyOutsideNamespace('App\Domain', ['Ramsey\Uuid'], true)) ->because('we want protect our domain except for Ramsey\Uuid'); ``` diff --git a/src/Analyzer/FileParser.php b/src/Analyzer/FileParser.php index d343fd97..7684f306 100644 --- a/src/Analyzer/FileParser.php +++ b/src/Analyzer/FileParser.php @@ -35,7 +35,7 @@ public function __construct( $lexer = new Emulative([ 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'], - 'phpVersion' => $targetPhpVersion->get() ?? phpversion(), + 'phpVersion' => $targetPhpVersion->get() ?? phpversion(), ]); $this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer); diff --git a/src/Analyzer/NameResolver.php b/src/Analyzer/NameResolver.php index 5842a3d3..47e5a49d 100644 --- a/src/Analyzer/NameResolver.php +++ b/src/Analyzer/NameResolver.php @@ -58,7 +58,7 @@ class NameResolver extends NodeVisitorAbstract * @param ErrorHandler|null $errorHandler Error handler * @param array $options Options */ - public function __construct(ErrorHandler $errorHandler = null, array $options = []) + public function __construct(?ErrorHandler $errorHandler = null, array $options = []) { $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; @@ -307,7 +307,7 @@ protected function resolveAttrGroups(Node $node): void } } - private function addAlias(Stmt\UseUse $use, int $type, Name $prefix = null): void + private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null): void { // Add prefix for group uses /** @var Name $name */ diff --git a/src/Expression/ForClasses/NotHaveDependencyOutsideNamespace.php b/src/Expression/ForClasses/NotHaveDependencyOutsideNamespace.php index 19ac84dd..906131ee 100644 --- a/src/Expression/ForClasses/NotHaveDependencyOutsideNamespace.php +++ b/src/Expression/ForClasses/NotHaveDependencyOutsideNamespace.php @@ -18,11 +18,14 @@ class NotHaveDependencyOutsideNamespace implements Expression private $namespace; /** @var array */ private $externalDependenciesToExclude; + /** @var bool */ + private $excludeCoreNamespace; - public function __construct(string $namespace, array $externalDependenciesToExclude = []) + public function __construct(string $namespace, array $externalDependenciesToExclude = [], bool $excludeCoreNamespace = false) { $this->namespace = $namespace; $this->externalDependenciesToExclude = $externalDependenciesToExclude; + $this->excludeCoreNamespace = $excludeCoreNamespace; } public function describe(ClassDescription $theClass, string $because): Description @@ -46,6 +49,10 @@ public function evaluate(ClassDescription $theClass, Violations $violations, str continue; } + if ($this->excludeCoreNamespace && '' === $externalDep->getFQCN()->namespace()) { + continue; + } + $violation = Violation::createWithErrorLine( $theClass->getFQCN(), ViolationMessage::withDescription( diff --git a/src/Rules/Violation.php b/src/Rules/Violation.php index f91d2a5e..dd1bfb8f 100644 --- a/src/Rules/Violation.php +++ b/src/Rules/Violation.php @@ -15,7 +15,7 @@ class Violation implements \JsonSerializable /** @var string */ private $error; - public function __construct(string $fqcn, string $error, int $line = null) + public function __construct(string $fqcn, string $error, ?int $line = null) { $this->fqcn = $fqcn; $this->error = $error; diff --git a/tests/E2E/Cli/CheckCommandTest.php b/tests/E2E/Cli/CheckCommandTest.php index ffc479f1..ec1389a1 100644 --- a/tests/E2E/Cli/CheckCommandTest.php +++ b/tests/E2E/Cli/CheckCommandTest.php @@ -148,8 +148,8 @@ public function test_you_can_ignore_the_default_baseline(): void protected function runCheck( $configFilePath = null, - bool $stopOnFailure = null, - string $useBaseline = null, + ?bool $stopOnFailure = null, + ?string $useBaseline = null, $generateBaseline = false, bool $skipBaseline = false ): ApplicationTester { @@ -181,7 +181,7 @@ protected function runCheck( return $appTester; } - protected function assertCheckHasErrors(ApplicationTester $applicationTester, string $expectedOutput = null): void + protected function assertCheckHasErrors(ApplicationTester $applicationTester, ?string $expectedOutput = null): void { $this->assertEquals(self::ERROR_CODE, $applicationTester->getStatusCode()); if (null != $expectedOutput) { @@ -191,7 +191,7 @@ protected function assertCheckHasErrors(ApplicationTester $applicationTester, st } } - protected function assertCheckHasNoErrorsLike(ApplicationTester $applicationTester, string $expectedOutput = null): void + protected function assertCheckHasNoErrorsLike(ApplicationTester $applicationTester, ?string $expectedOutput = null): void { $this->assertEquals(self::ERROR_CODE, $applicationTester->getStatusCode()); if (null != $expectedOutput) { diff --git a/tests/Unit/Expressions/ForClasses/NotHaveDependencyOutsideNamespaceTest.php b/tests/Unit/Expressions/ForClasses/NotHaveDependencyOutsideNamespaceTest.php index 3105c69a..f2322cbf 100644 --- a/tests/Unit/Expressions/ForClasses/NotHaveDependencyOutsideNamespaceTest.php +++ b/tests/Unit/Expressions/ForClasses/NotHaveDependencyOutsideNamespaceTest.php @@ -64,7 +64,11 @@ public function test_it_should_return_false_if_depends_on_namespace(): void $notHaveDependencyOutsideNamespace = new NotHaveDependencyOutsideNamespace('myNamespace'); $classDescription = new ClassDescription( FullyQualifiedClassName::fromString('HappyIsland'), - [new ClassDependency('myNamespace', 100), new ClassDependency('another\class', 200)], + [ + new ClassDependency('myNamespace', 100), + new ClassDependency('another\class', 200), + new ClassDependency('\DateTime', 300), + ], [], null, false, @@ -77,7 +81,7 @@ public function test_it_should_return_false_if_depends_on_namespace(): void $because = 'we want to add this rule for our software'; $violations = new Violations(); $notHaveDependencyOutsideNamespace->evaluate($classDescription, $violations, $because); - self::assertNotEquals(0, $violations->count()); + self::assertEquals(2, $violations->count()); } public function test_it_should_not_return_violation_error_if_dependency_excluded(): void @@ -100,4 +104,25 @@ public function test_it_should_not_return_violation_error_if_dependency_excluded $notHaveDependencyOutsideNamespace->evaluate($classDescription, $violations, $because); self::assertEquals(0, $violations->count()); } + + public function test_it_should_not_return_violation_error_if_core_dependency_excluded(): void + { + $notHaveDependencyOutsideNamespace = new NotHaveDependencyOutsideNamespace('myNamespace', [], true); + $classDescription = new ClassDescription( + FullyQualifiedClassName::fromString('HappyIsland'), + [new ClassDependency('\DateTime', 100)], + [], + null, + false, + false, + false, + false, + false, + false + ); + $because = 'we want to add this rule for our software'; + $violations = new Violations(); + $notHaveDependencyOutsideNamespace->evaluate($classDescription, $violations, $because); + self::assertEquals(0, $violations->count()); + } }