diff --git a/src/Analyser/ClassCollector.php b/src/Analyser/ClassCollector.php index 40f46289..c8d7529e 100644 --- a/src/Analyser/ClassCollector.php +++ b/src/Analyser/ClassCollector.php @@ -30,13 +30,14 @@ use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Property; use PhpParser\Node\Stmt\Trait_; use PhpParser\Node\Stmt\TraitUse; +use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\While_; -use PhpParser\Node\UseItem; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; @@ -99,8 +100,18 @@ public function getNodes(): array public function enterNode(Node $node): null { - if ($node instanceof UseItem) { - $this->fileUses[] = $node->name->toString(); + if ($node instanceof Use_) { + foreach ($node->uses as $use) { + $this->fileUses[] = $use->name->toString(); + } + } + + if ($node instanceof GroupUse) { + $prefix = $node->prefix->toString(); + + foreach ($node->uses as $use) { + $this->fileUses[] = $prefix . '\\' . $use->name->toString(); + } } if ($node instanceof Function_ && isset($node->namespacedName)) { diff --git a/tests/Analyser/AnalyserTest.php b/tests/Analyser/AnalyserTest.php index 4c1d2caf..9403640d 100644 --- a/tests/Analyser/AnalyserTest.php +++ b/tests/Analyser/AnalyserTest.php @@ -17,9 +17,11 @@ use Boundwize\StructArmed\Rule\RuleViolation; use Boundwize\StructArmed\Tests\Support\TemporaryDirectoryCleanupTrait; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function array_map; +use function count; use function dirname; use function file_put_contents; use function is_dir; @@ -770,6 +772,126 @@ public function __construct(private QueryBuilder $db) {} $this->assertStringContainsString('Database', $violations[0]->message); } + /** + * @return iterable}> + */ + public static function importedSymbolDependencyProvider(): iterable + { + yield 'constant fetch' => [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + $dependencies + */ + #[DataProvider('importedSymbolDependencyProvider')] + public function testAnalyserRulesetTreatsImportedConstantsAndFunctionsAsDependencies( + string $sourceCode, + array $dependencies + ): void { + $basePath = $this->makeTempProject([ + 'src/HTTP/Request.php' => $sourceCode, + ]); + + $architecture = Architecture::define() + ->layerPattern('HTTP', '/^App\\\\HTTP\\\\.*$/') + ->layerPattern('Database', '/^App\\\\Database\\\\.*$/') + ->ruleset([ + 'HTTP' => [], + ]); + + $ruleViolationCollection = (new Analyser($basePath))->analyse($architecture, ['src/']); + + $violations = $ruleViolationCollection->forRule('ruleset.HTTP'); + + $this->assertCount(count($dependencies), $violations); + + foreach ($dependencies as $index => $dependency) { + $this->assertStringContainsString($dependency, $violations[$index]->message); + $this->assertStringContainsString('Database', $violations[$index]->message); + } + } + public function testAnalyserRulesetAllowsListedLayerDependency(): void { $basePath = $this->makeTempProject([ diff --git a/tests/Analyser/ClassCollectorTest.php b/tests/Analyser/ClassCollectorTest.php index 471f1182..bec50e55 100644 --- a/tests/Analyser/ClassCollectorTest.php +++ b/tests/Analyser/ClassCollectorTest.php @@ -11,6 +11,7 @@ use PhpParser\NodeVisitor\NameResolver; use PhpParser\ParserFactory; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use function getcwd; @@ -424,6 +425,109 @@ class Foo {} $this->assertContains('App\Domain\Order', $classNode->dependencies); } + /** + * @return iterable}> + */ + public static function groupedImportDependencyProvider(): iterable + { + yield 'class imports' => [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + $expectedDependencies + */ + #[DataProvider('groupedImportDependencyProvider')] + public function testCollectsGroupedImportedUsageAsDependenciesWithoutShortNames( + string $code, + array $expectedDependencies + ): void { + $classNode = $this->collect($code); + + $this->assertSame($expectedDependencies, $classNode->dependencies); + } + + public function testCollectsImportedConstantUsageAsDependency(): void + { + $code = <<<'PHP' +collect($code); + + $this->assertContains('App\Infrastructure\Config\FEATURE_ENABLED', $classNode->dependencies); + } + public function testCollectsFullyQualifiedDependencies(): void { $classNode = $this->collect('