From 581d3e305ebda7834d8e62b97a3ee86dc8cd13ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20M=C3=B6nch?= Date: Fri, 26 Mar 2021 16:50:54 +0100 Subject: [PATCH 1/3] adds debug commands for layer and class-likes --- .phive/phars.xml | 8 +- config/services.php | 48 +++++++++++- src/Analyser.php | 22 ++---- src/ClassLikeAnalyser.php | 38 +++++++++ ...esolver.php => ClassLikeLayerResolver.php} | 4 +- src/ClassLikeLayerResolverFactory.php | 34 ++++++++ ...hp => ClassLikeLayerResolverInterface.php} | 4 +- src/ClassNameLayerResolverCacheDecorator.php | 32 -------- src/Console/Application.php | 10 ++- src/Console/Command/DebugClassLikeCommand.php | 68 ++++++++++++++++ src/Console/Command/DebugLayerCommand.php | 77 +++++++++++++++++++ src/LayerAnalyser.php | 64 +++++++++++++++ src/MemoizedClassLikeLayerResolver.php | 29 +++++++ src/RulesetEngine.php | 6 +- ...est.php => ClassLikeLayerResolverTest.php} | 14 ++-- ...assNameLayerResolverCacheDecoratorTest.php | 25 ------ tests/MemoizedClassLikeLayerResolverTest.php | 25 ++++++ tests/RulesetEngineTest.php | 20 ++--- 18 files changed, 423 insertions(+), 105 deletions(-) create mode 100644 src/ClassLikeAnalyser.php rename src/{ClassNameLayerResolver.php => ClassLikeLayerResolver.php} (93%) create mode 100644 src/ClassLikeLayerResolverFactory.php rename src/{ClassNameLayerResolverInterface.php => ClassLikeLayerResolverInterface.php} (56%) delete mode 100644 src/ClassNameLayerResolverCacheDecorator.php create mode 100644 src/Console/Command/DebugClassLikeCommand.php create mode 100644 src/Console/Command/DebugLayerCommand.php create mode 100644 src/LayerAnalyser.php create mode 100644 src/MemoizedClassLikeLayerResolver.php rename tests/{ClassNameLayerResolverTest.php => ClassLikeLayerResolverTest.php} (86%) delete mode 100644 tests/ClassNameLayerResolverCacheDecoratorTest.php create mode 100644 tests/MemoizedClassLikeLayerResolverTest.php diff --git a/.phive/phars.xml b/.phive/phars.xml index 9d04d16a4..45af0ec7f 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,9 +1,9 @@ - - - - + + + + diff --git a/config/services.php b/config/services.php index 66a6c98cb..81eb56f6c 100644 --- a/config/services.php +++ b/config/services.php @@ -14,6 +14,8 @@ use Qossmic\Deptrac\AstRunner\Resolver\ClassConstantResolver; use Qossmic\Deptrac\AstRunner\Resolver\PropertyTypeResolver; use Qossmic\Deptrac\AstRunner\Resolver\TypeResolver; +use Qossmic\Deptrac\ClassLikeAnalyser; +use Qossmic\Deptrac\ClassLikeLayerResolverFactory; use Qossmic\Deptrac\Collector\BoolCollector; use Qossmic\Deptrac\Collector\ClassNameCollector; use Qossmic\Deptrac\Collector\ClassNameRegexCollector; @@ -30,12 +32,15 @@ use Qossmic\Deptrac\Configuration\Loader\YmlFileLoader; use Qossmic\Deptrac\Configuration\ParameterResolver; use Qossmic\Deptrac\Console\Command\AnalyzeCommand; +use Qossmic\Deptrac\Console\Command\DebugClassLikeCommand; +use Qossmic\Deptrac\Console\Command\DebugLayerCommand; use Qossmic\Deptrac\Console\Command\InitCommand; use Qossmic\Deptrac\Dependency\InheritanceFlatter; use Qossmic\Deptrac\Dependency\Resolver; use Qossmic\Deptrac\DependencyEmitter\BasicDependencyEmitter; use Qossmic\Deptrac\DependencyEmitter\InheritanceDependencyEmitter; use Qossmic\Deptrac\FileResolver; +use Qossmic\Deptrac\LayerAnalyser; use Qossmic\Deptrac\OutputFormatter\BaselineOutputFormatter; use Qossmic\Deptrac\OutputFormatter\ConsoleOutputFormatter; use Qossmic\Deptrac\OutputFormatter\GithubActionsOutputFormatter; @@ -94,15 +99,38 @@ ->set(PropertyTypeResolver::class) ->args([service(TypeResolver::class)]); + $services + ->set(ClassLikeLayerResolverFactory::class) + ->args([ + service(Registry::class), + service(ParameterResolver::class), + ]); + $services ->set(Analyser::class) ->args([ service(AstRunner::class), service(FileResolver::class), service(Resolver::class), - service(Registry::class), service(RulesetEngine::class), - service(ParameterResolver::class), + service(ClassLikeLayerResolverFactory::class), + ]); + + $services + ->set(ClassLikeAnalyser::class) + ->args([ + service(AstRunner::class), + service(FileResolver::class), + service(ClassLikeLayerResolverFactory::class), + ]); + + $services + ->set(LayerAnalyser::class) + ->args([ + service(AstRunner::class), + service(FileResolver::class), + service(Resolver::class), + service(ClassLikeLayerResolverFactory::class), ]); $services->set(RulesetEngine::class); @@ -212,4 +240,20 @@ service(OutputFormatterFactory::class), ]) ->public(); + + $services + ->set(DebugClassLikeCommand::class) + ->args([ + service(ClassLikeAnalyser::class), + service(Loader::class), + ]) + ->public(); + + $services + ->set(DebugLayerCommand::class) + ->args([ + service(LayerAnalyser::class), + service(Loader::class), + ]) + ->public(); }; diff --git a/src/Analyser.php b/src/Analyser.php index 3e83c4bcb..bfc16d96a 100644 --- a/src/Analyser.php +++ b/src/Analyser.php @@ -3,9 +3,7 @@ namespace Qossmic\Deptrac; use Qossmic\Deptrac\AstRunner\AstRunner; -use Qossmic\Deptrac\Collector\Registry; use Qossmic\Deptrac\Configuration\Configuration; -use Qossmic\Deptrac\Configuration\ParameterResolver; use Qossmic\Deptrac\Dependency\Resolver; use Qossmic\Deptrac\RulesetEngine\Context; @@ -14,39 +12,29 @@ class Analyser private $astRunner; private $fileResolver; private $resolver; - private $collectorRegistry; private $rulesetEngine; - private $parameterResolver; + private $classLikeLayerResolverFactory; public function __construct( AstRunner $astRunner, FileResolver $fileResolver, Resolver $resolver, - Registry $collectorRegistry, RulesetEngine $rulesetEngine, - ParameterResolver $parameterResolver + ClassLikeLayerResolverFactory $classLikeLayerResolverFactory ) { $this->astRunner = $astRunner; $this->fileResolver = $fileResolver; $this->resolver = $resolver; - $this->collectorRegistry = $collectorRegistry; $this->rulesetEngine = $rulesetEngine; - $this->parameterResolver = $parameterResolver; + $this->classLikeLayerResolverFactory = $classLikeLayerResolverFactory; } public function analyse(Configuration $configuration): Context { $astMap = $this->astRunner->createAstMapByFiles($this->fileResolver->resolve($configuration)); $dependencyResult = $this->resolver->resolve($astMap); + $classLikeLayerResolver = $this->classLikeLayerResolverFactory->create($configuration, $astMap); - $classNameLayerResolver = new ClassNameLayerResolverCacheDecorator( - new ClassNameLayerResolver($configuration, $astMap, $this->collectorRegistry, $this->parameterResolver) - ); - - return $this->rulesetEngine->process( - $dependencyResult, - $classNameLayerResolver, - $configuration - ); + return $this->rulesetEngine->process($dependencyResult, $classLikeLayerResolver, $configuration); } } diff --git a/src/ClassLikeAnalyser.php b/src/ClassLikeAnalyser.php new file mode 100644 index 000000000..907a70aff --- /dev/null +++ b/src/ClassLikeAnalyser.php @@ -0,0 +1,38 @@ +astRunner = $astRunner; + $this->fileResolver = $fileResolver; + $this->classLikeLayerResolverFactory = $classLikeLayerResolverFactory; + } + + /** + * @return string[] + */ + public function analyse(Configuration $configuration, ClassLikeName $classLikeName): array + { + $astMap = $this->astRunner->createAstMapByFiles($this->fileResolver->resolve($configuration)); + + return $this->classLikeLayerResolverFactory + ->create($configuration, $astMap) + ->getLayersByClassLikeName($classLikeName); + } +} diff --git a/src/ClassNameLayerResolver.php b/src/ClassLikeLayerResolver.php similarity index 93% rename from src/ClassNameLayerResolver.php rename to src/ClassLikeLayerResolver.php index 1eeb0ec4e..5b781b97b 100644 --- a/src/ClassNameLayerResolver.php +++ b/src/ClassLikeLayerResolver.php @@ -11,7 +11,7 @@ use Qossmic\Deptrac\Configuration\Configuration; use Qossmic\Deptrac\Configuration\ParameterResolver; -class ClassNameLayerResolver implements ClassNameLayerResolverInterface +class ClassLikeLayerResolver implements ClassLikeLayerResolverInterface { private $configuration; private $astMap; @@ -33,7 +33,7 @@ public function __construct( /** * @return string[] */ - public function getLayersByClassName(ClassLikeName $className): array + public function getLayersByClassLikeName(ClassLikeName $className): array { /** @var array $layers */ $layers = []; diff --git a/src/ClassLikeLayerResolverFactory.php b/src/ClassLikeLayerResolverFactory.php new file mode 100644 index 000000000..b7336a127 --- /dev/null +++ b/src/ClassLikeLayerResolverFactory.php @@ -0,0 +1,34 @@ +registry = $registry; + $this->parameterResolver = $parameterResolver; + } + + public function create(Configuration $configuration, AstMap $astMap): ClassLikeLayerResolverInterface + { + return new MemoizedClassLikeLayerResolver( + new ClassLikeLayerResolver( + $configuration, + $astMap, + $this->registry, + $this->parameterResolver + ) + ); + } +} diff --git a/src/ClassNameLayerResolverInterface.php b/src/ClassLikeLayerResolverInterface.php similarity index 56% rename from src/ClassNameLayerResolverInterface.php rename to src/ClassLikeLayerResolverInterface.php index b10e002df..cef2cfc63 100644 --- a/src/ClassNameLayerResolverInterface.php +++ b/src/ClassLikeLayerResolverInterface.php @@ -6,10 +6,10 @@ use Qossmic\Deptrac\AstRunner\AstMap\ClassLikeName; -interface ClassNameLayerResolverInterface +interface ClassLikeLayerResolverInterface { /** * @return string[] */ - public function getLayersByClassName(ClassLikeName $className): array; + public function getLayersByClassLikeName(ClassLikeName $className): array; } diff --git a/src/ClassNameLayerResolverCacheDecorator.php b/src/ClassNameLayerResolverCacheDecorator.php deleted file mode 100644 index b7bff12e3..000000000 --- a/src/ClassNameLayerResolverCacheDecorator.php +++ /dev/null @@ -1,32 +0,0 @@ - */ - private $layerNamesByClassCache = []; - - /** @var string[] */ - private $layerNamesCache = []; - - public function __construct(ClassNameLayerResolverInterface $classNameLayerResolver) - { - $this->classNameLayerResolver = $classNameLayerResolver; - } - - public function getLayersByClassName(ClassLikeName $className): array - { - if (!isset($this->layerNamesByClassCache[$className->toString()])) { - $this->layerNamesByClassCache[$className->toString()] = $this->classNameLayerResolver->getLayersByClassName($className); - } - - return $this->layerNamesByClassCache[$className->toString()]; - } -} diff --git a/src/Console/Application.php b/src/Console/Application.php index 18f71c517..e7c153bb2 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -5,6 +5,8 @@ namespace Qossmic\Deptrac\Console; use Qossmic\Deptrac\Console\Command\AnalyzeCommand; +use Qossmic\Deptrac\Console\Command\DebugClassLikeCommand; +use Qossmic\Deptrac\Console\Command\DebugLayerCommand; use Qossmic\Deptrac\Console\Command\InitCommand; use Qossmic\Deptrac\ShouldNotHappenException; use Symfony\Component\Config\FileLocator; @@ -45,7 +47,13 @@ public function doRun(InputInterface $input, OutputInterface $output): int /** @var AnalyzeCommand $analyzeCommand */ $analyzeCommand = $container->get(AnalyzeCommand::class); - $this->addCommands([$initCommand, $analyzeCommand]); + /** @var DebugClassLikeCommand $debugClassLikeCommand */ + $debugClassLikeCommand = $container->get(DebugClassLikeCommand::class); + + /** @var DebugLayerCommand $debugLayerCommand */ + $debugLayerCommand = $container->get(DebugLayerCommand::class); + + $this->addCommands([$initCommand, $analyzeCommand, $debugClassLikeCommand, $debugLayerCommand]); $this->setDefaultCommand('analyze'); return parent::doRun($input, $output); diff --git a/src/Console/Command/DebugClassLikeCommand.php b/src/Console/Command/DebugClassLikeCommand.php new file mode 100644 index 000000000..7d51dfeea --- /dev/null +++ b/src/Console/Command/DebugClassLikeCommand.php @@ -0,0 +1,68 @@ +analyser = $analyser; + $this->loader = $loader; + } + + protected function configure(): void + { + $this->addArgument('depfile', InputArgument::REQUIRED, 'Path to the depfile'); + $this->addArgument('class-like', InputArgument::REQUIRED, 'Full qualified class-like name to debug'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $style = new Style(new SymfonyStyle($input, $output)); + + $depfile = $input->getArgument('depfile'); + + if (!is_string($depfile)) { + throw SingleDepfileIsRequiredException::fromArgument($depfile); + } + + /** @var string $classLike */ + $classLike = $input->getArgument('class-like'); + + $configuration = $this->loader->load($depfile); + + $matchedClassLikeNames = $this->analyser->analyse($configuration, ClassLikeName::fromFQCN($classLike)); + + natcasesort($matchedClassLikeNames); + + $style->table([$classLike], array_map(static function (string $matchedClassLikeName): array { + return [$matchedClassLikeName]; + }, $matchedClassLikeNames)); + + return 0; + } +} diff --git a/src/Console/Command/DebugLayerCommand.php b/src/Console/Command/DebugLayerCommand.php new file mode 100644 index 000000000..5beb34d35 --- /dev/null +++ b/src/Console/Command/DebugLayerCommand.php @@ -0,0 +1,77 @@ +analyser = $analyser; + $this->loader = $loader; + } + + protected function configure(): void + { + $this->addArgument('depfile', InputArgument::REQUIRED, 'Path to the depfile'); + $this->addArgument('layer', InputArgument::REQUIRED, 'Layer to debug'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $style = new Style(new SymfonyStyle($input, $output)); + + $depfile = $input->getArgument('depfile'); + + if (!is_string($depfile)) { + throw SingleDepfileIsRequiredException::fromArgument($depfile); + } + + /** @var string $layer */ + $layer = $input->getArgument('layer'); + + $configuration = $this->loader->load($depfile); + + $configurationLayers = array_map(static function (ConfigurationLayer $configurationLayer) { + return $configurationLayer->getName(); + }, $configuration->getLayers()); + + if (!in_array($layer, $configurationLayers, true)) { + $style->error('Layer not found.'); + + return 1; + } + + $matchedLayers = $this->analyser->analyse($configuration, $layer); + natcasesort($matchedLayers); + + $style->table([$layer], array_map(static function (string $matchedLayer): array { + return [$matchedLayer]; + }, $matchedLayers)); + + return 0; + } +} diff --git a/src/LayerAnalyser.php b/src/LayerAnalyser.php new file mode 100644 index 000000000..9b4fcfe4f --- /dev/null +++ b/src/LayerAnalyser.php @@ -0,0 +1,64 @@ +astRunner = $astRunner; + $this->fileResolver = $fileResolver; + $this->resolver = $resolver; + $this->classLikeLayerResolverFactory = $classLikeLayerResolverFactory; + } + + /** + * @return string[] + */ + public function analyse(Configuration $configuration, string $layer): array + { + $astMap = $this->astRunner->createAstMapByFiles($this->fileResolver->resolve($configuration)); + $dependencyResult = $this->resolver->resolve($astMap); + $classLikeLayerResolver = $this->classLikeLayerResolverFactory->create($configuration, $astMap); + + /** @var string[] $classLikeNames */ + $classLikeNames = []; + foreach ($dependencyResult->getDependenciesAndInheritDependencies() as $dependency) { + $classLikeName = $dependency->getClassLikeNameA(); + if ($this->isInLayer($layer, $classLikeName, $classLikeLayerResolver)) { + $classLikeNames[] = $classLikeName->toString(); + } + + $classLikeName = $dependency->getClassLikeNameB(); + if ($this->isInLayer($layer, $classLikeName, $classLikeLayerResolver)) { + $classLikeNames[] = $classLikeName->toString(); + } + } + + return array_unique($classLikeNames); + } + + private function isInLayer( + string $layer, + ClassLikeName $classLikeName, + ClassLikeLayerResolverInterface $classLikeLayerResolver + ): bool { + return in_array($layer, $classLikeLayerResolver->getLayersByClassLikeName($classLikeName), true); + } +} diff --git a/src/MemoizedClassLikeLayerResolver.php b/src/MemoizedClassLikeLayerResolver.php new file mode 100644 index 000000000..061043691 --- /dev/null +++ b/src/MemoizedClassLikeLayerResolver.php @@ -0,0 +1,29 @@ + */ + private $layerNamesByClassCache = []; + + public function __construct(ClassLikeLayerResolverInterface $classLikeLayerResolver) + { + $this->classLikeLayerResolver = $classLikeLayerResolver; + } + + public function getLayersByClassLikeName(ClassLikeName $className): array + { + if (!isset($this->layerNamesByClassCache[$className->toString()])) { + $this->layerNamesByClassCache[$className->toString()] = $this->classLikeLayerResolver->getLayersByClassLikeName($className); + } + + return $this->layerNamesByClassCache[$className->toString()]; + } +} diff --git a/src/RulesetEngine.php b/src/RulesetEngine.php index dfd55c25f..7cd84f4de 100644 --- a/src/RulesetEngine.php +++ b/src/RulesetEngine.php @@ -21,7 +21,7 @@ class RulesetEngine { public function process( Result $dependencyResult, - ClassNameLayerResolverInterface $classNameLayerResolver, + ClassLikeLayerResolverInterface $classLikeLayerResolver, Configuration $configuration ): Context { $rules = []; @@ -31,7 +31,7 @@ public function process( $skippedViolationHelper = new SkippedViolationHelper($configuration->getSkipViolations()); foreach ($dependencyResult->getDependenciesAndInheritDependencies() as $dependency) { - $layerNames = $classNameLayerResolver->getLayersByClassName($dependency->getClassLikeNameA()); + $layerNames = $classLikeLayerResolver->getLayersByClassLikeName($dependency->getClassLikeNameA()); $classLikeANameString = $dependency->getClassLikeNameA()->toString(); if (!isset($warnings[$classLikeANameString]) && count($layerNames) > 1) { @@ -41,7 +41,7 @@ public function process( foreach ($layerNames as $layerName) { $allowedDependencies = $configurationRuleset->getAllowedDependencies($layerName); - $layersNamesClassB = $classNameLayerResolver->getLayersByClassName($dependency->getClassLikeNameB()); + $layersNamesClassB = $classLikeLayerResolver->getLayersByClassLikeName($dependency->getClassLikeNameB()); if (0 === count($layersNamesClassB)) { if (!$this->ignoreUncoveredInternalClass($configuration, $dependency->getClassLikeNameB())) { diff --git a/tests/ClassNameLayerResolverTest.php b/tests/ClassLikeLayerResolverTest.php similarity index 86% rename from tests/ClassNameLayerResolverTest.php rename to tests/ClassLikeLayerResolverTest.php index 5be0e9377..180b40e3a 100644 --- a/tests/ClassNameLayerResolverTest.php +++ b/tests/ClassLikeLayerResolverTest.php @@ -9,14 +9,14 @@ use Qossmic\Deptrac\AstRunner\AstMap; use Qossmic\Deptrac\AstRunner\AstMap\AstClassReference; use Qossmic\Deptrac\AstRunner\AstMap\ClassLikeName; -use Qossmic\Deptrac\ClassNameLayerResolver; +use Qossmic\Deptrac\ClassLikeLayerResolver; use Qossmic\Deptrac\Collector\CollectorInterface; use Qossmic\Deptrac\Collector\Registry; use Qossmic\Deptrac\Configuration\Configuration; use Qossmic\Deptrac\Configuration\ConfigurationLayer; use Qossmic\Deptrac\Configuration\ParameterResolver; -final class ClassNameLayerResolverTest extends TestCase +final class ClassLikeLayerResolverTest extends TestCase { private function getCollector(bool $return) { @@ -31,7 +31,7 @@ private function getCollector(bool $return) return $collector->reveal(); } - public function provideGetLayersByClassName(): iterable + public function provideGetLayersByClassLikeName(): iterable { yield [ true, @@ -77,9 +77,9 @@ public function provideGetLayersByClassName(): iterable } /** - * @dataProvider provideGetLayersByClassName + * @dataProvider provideGetLayersByClassLikeName */ - public function testGetLayersByClassName(bool $collectA, bool $collectB1, bool $collectB2, array $expectedLayers): void + public function testGetLayersByClassLikeName(bool $collectA, bool $collectB1, bool $collectB2, array $expectedLayers): void { $configuration = $this->prophesize(Configuration::class); $configuration->getLayers()->willReturn([ @@ -111,7 +111,7 @@ public function testGetLayersByClassName(bool $collectA, bool $collectB1, bool $ $this->getCollector($collectB2) ); - $resolver = new ClassNameLayerResolver( + $resolver = new ClassLikeLayerResolver( $configuration->reveal(), $astMap->reveal(), $collectorRegistry->reveal(), @@ -120,7 +120,7 @@ public function testGetLayersByClassName(bool $collectA, bool $collectB1, bool $ self::assertEquals( $expectedLayers, - $resolver->getLayersByClassName(ClassLikeName::fromFQCN('classA')) + $resolver->getLayersByClassLikeName(ClassLikeName::fromFQCN('classA')) ); } } diff --git a/tests/ClassNameLayerResolverCacheDecoratorTest.php b/tests/ClassNameLayerResolverCacheDecoratorTest.php deleted file mode 100644 index bfe8aa0c5..000000000 --- a/tests/ClassNameLayerResolverCacheDecoratorTest.php +++ /dev/null @@ -1,25 +0,0 @@ -prophesize(ClassNameLayerResolverInterface::class); - $decorated->getLayersByClassName($classLikeName)->willReturn(['bar']); - - $decorator = new ClassNameLayerResolverCacheDecorator($decorated->reveal()); - - self::assertEquals(['bar'], $decorator->getLayersByClassName($classLikeName)); - self::assertEquals(['bar'], $decorator->getLayersByClassName($classLikeName)); - } -} diff --git a/tests/MemoizedClassLikeLayerResolverTest.php b/tests/MemoizedClassLikeLayerResolverTest.php new file mode 100644 index 000000000..f9e628ab2 --- /dev/null +++ b/tests/MemoizedClassLikeLayerResolverTest.php @@ -0,0 +1,25 @@ +prophesize(ClassLikeLayerResolverInterface::class); + $decorated->getLayersByClassLikeName($classLikeName)->willReturn(['bar']); + + $decorator = new MemoizedClassLikeLayerResolver($decorated->reveal()); + + self::assertEquals(['bar'], $decorator->getLayersByClassLikeName($classLikeName)); + self::assertEquals(['bar'], $decorator->getLayersByClassLikeName($classLikeName)); + } +} diff --git a/tests/RulesetEngineTest.php b/tests/RulesetEngineTest.php index 68522fe64..03ecc8e9b 100644 --- a/tests/RulesetEngineTest.php +++ b/tests/RulesetEngineTest.php @@ -7,7 +7,7 @@ use PHPUnit\Framework\TestCase; use Qossmic\Deptrac\AstRunner\AstMap\ClassLikeName; use Qossmic\Deptrac\AstRunner\AstMap\FileOccurrence; -use Qossmic\Deptrac\ClassNameLayerResolverInterface; +use Qossmic\Deptrac\ClassLikeLayerResolverInterface; use Qossmic\Deptrac\Configuration\Configuration; use Qossmic\Deptrac\Dependency\Dependency; use Qossmic\Deptrac\Dependency\Result; @@ -234,9 +234,9 @@ public function testProcess( $dependencyResult->addDependency($dep); } - $classNameLayerResolver = $this->prophesize(ClassNameLayerResolverInterface::class); + $classLikeLayerResolver = $this->prophesize(ClassLikeLayerResolverInterface::class); foreach ($classesInLayers as $classInLayer => $layers) { - $classNameLayerResolver->getLayersByClassName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); + $classLikeLayerResolver->getLayersByClassLikeName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); } $configuration = Configuration::fromArray([ @@ -247,7 +247,7 @@ public function testProcess( $context = (new RulesetEngine())->process( $dependencyResult, - $classNameLayerResolver->reveal(), + $classLikeLayerResolver->reveal(), $configuration ); @@ -301,9 +301,9 @@ public function testGetSkippedViolations(array $dependenciesAsArray, array $clas $dependencyResult->addDependency($dep); } - $classNameLayerResolver = $this->prophesize(ClassNameLayerResolverInterface::class); + $classLikeLayerResolver = $this->prophesize(ClassLikeLayerResolverInterface::class); foreach ($classesInLayers as $classInLayer => $layers) { - $classNameLayerResolver->getLayersByClassName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); + $classLikeLayerResolver->getLayersByClassLikeName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); } $configuration = Configuration::fromArray([ @@ -315,7 +315,7 @@ public function testGetSkippedViolations(array $dependenciesAsArray, array $clas $context = (new RulesetEngine())->process( $dependencyResult, - $classNameLayerResolver->reveal(), + $classLikeLayerResolver->reveal(), $configuration ); @@ -355,9 +355,9 @@ public function testIgnoreUncoveredInternalClasses(array $dependenciesAsArray, a $dependencyResult->addDependency($dep); } - $classNameLayerResolver = $this->prophesize(ClassNameLayerResolverInterface::class); + $classLikeLayerResolver = $this->prophesize(ClassLikeLayerResolverInterface::class); foreach ($classesInLayers as $classInLayer => $layers) { - $classNameLayerResolver->getLayersByClassName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); + $classLikeLayerResolver->getLayersByClassLikeName(ClassLikeName::fromFQCN($classInLayer))->willReturn($layers); } $configuration = Configuration::fromArray([ @@ -370,7 +370,7 @@ public function testIgnoreUncoveredInternalClasses(array $dependenciesAsArray, a $context = (new RulesetEngine())->process( $dependencyResult, - $classNameLayerResolver->reveal(), + $classLikeLayerResolver->reveal(), $configuration ); From 4989ff93a4db708c564a0901ed179dfa414f524f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20M=C3=B6nch?= Date: Fri, 9 Apr 2021 09:55:48 +0200 Subject: [PATCH 2/3] make psalm happy again --- baseline.xml | 35 ++++++++++--------- src/Configuration/ParameterResolver.php | 17 +++++---- .../BasicDependencyEmitter.php | 2 -- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/baseline.xml b/baseline.xml index 473dd2d13..1627ceeb7 100644 --- a/baseline.xml +++ b/baseline.xml @@ -1,5 +1,10 @@ + + + array<string, array{hash: string, reference: AstFileReference}> + + $className @@ -31,11 +36,6 @@ self::$classAstMap[$classLikeName] ?? null - - - $configuration - - $v @@ -147,6 +147,10 @@ children + + $remove + $removeKeyItem + @@ -166,20 +170,9 @@ - + $value - - - $keys - array_values($parameters) - - $value - - - $values - array<array-key, string|array> - @@ -208,6 +201,9 @@ $dumpHtmlPath $dumpImagePath + + $layerDependOnCount + @@ -227,6 +223,11 @@ $dumpXmlPath + + + $formatter + + $filename diff --git a/src/Configuration/ParameterResolver.php b/src/Configuration/ParameterResolver.php index 87776de3d..076882dc8 100644 --- a/src/Configuration/ParameterResolver.php +++ b/src/Configuration/ParameterResolver.php @@ -7,10 +7,10 @@ final class ParameterResolver { /** - * @param array $values - * @param array $parameters + * @param array $values + * @param array $parameters * - * @return array + * @return array */ public function resolve(array $values, array $parameters): array { @@ -22,11 +22,16 @@ public function resolve(array $values, array $parameters): array return "%$key%"; }, array_keys($parameters)); - $values = $this->replace($values, $keys, $parameters); - - return $values; + return $this->replace($values, $keys, $parameters); } + /** + * @param array $values + * @param string[] $keys + * @param array $parameters + * + * @return array + */ private function replace(array $values, array $keys, array $parameters): array { foreach ($values as &$value) { diff --git a/src/DependencyEmitter/BasicDependencyEmitter.php b/src/DependencyEmitter/BasicDependencyEmitter.php index 131d6b86e..bc0fcc485 100644 --- a/src/DependencyEmitter/BasicDependencyEmitter.php +++ b/src/DependencyEmitter/BasicDependencyEmitter.php @@ -5,7 +5,6 @@ namespace Qossmic\Deptrac\DependencyEmitter; use Qossmic\Deptrac\AstRunner\AstMap; -use Qossmic\Deptrac\AstRunner\AstMap\AstDependency; use Qossmic\Deptrac\Dependency\Dependency; use Qossmic\Deptrac\Dependency\Result; @@ -22,7 +21,6 @@ public function applyDependencies(AstMap $astMap, Result $dependencyResult): voi $uses = $fileReference->getDependencies(); foreach ($fileReference->getAstClassReferences() as $astClassReference) { - /** @var AstDependency[] $dependencies */ $dependencies = array_merge($uses, $astClassReference->getDependencies()); foreach ($dependencies as $emittedDependency) { From 18208159f6a0911870373fb2fd1ae98272f969cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20M=C3=B6nch?= Date: Fri, 9 Apr 2021 14:29:48 +0200 Subject: [PATCH 3/3] adds tests --- config/services.php | 14 ++--- src/Console/Application.php | 31 +++-------- src/ContainerBuilder.php | 53 +++++++++++++++++++ src/LayerAnalyser.php | 8 +++ tests/ClassLikeAnalyserTest.php | 41 ++++++++++++++ tests/Fixtures/ClassLikeAnalyser/ClassFoo.php | 10 ++++ tests/Fixtures/LayerAnalyser/ClassBar.php | 10 ++++ tests/Fixtures/LayerAnalyser/ClassFoo.php | 10 ++++ tests/LayerAnalyserTest.php | 47 ++++++++++++++++ 9 files changed, 192 insertions(+), 32 deletions(-) create mode 100644 src/ContainerBuilder.php create mode 100644 tests/ClassLikeAnalyserTest.php create mode 100644 tests/Fixtures/ClassLikeAnalyser/ClassFoo.php create mode 100644 tests/Fixtures/LayerAnalyser/ClassBar.php create mode 100644 tests/Fixtures/LayerAnalyser/ClassFoo.php create mode 100644 tests/LayerAnalyserTest.php diff --git a/config/services.php b/config/services.php index 81eb56f6c..60f6dda44 100644 --- a/config/services.php +++ b/config/services.php @@ -60,7 +60,7 @@ $services = $container->services(); $services->defaults() - ->private(); + ->public(); $services->set(EventDispatcher::class); @@ -228,8 +228,7 @@ /* Commands */ $services ->set(InitCommand::class) - ->args([service(Dumper::class)]) - ->public(); + ->args([service(Dumper::class)]); $services ->set(AnalyzeCommand::class) @@ -238,22 +237,19 @@ service(Loader::class), service(EventDispatcher::class), service(OutputFormatterFactory::class), - ]) - ->public(); + ]); $services ->set(DebugClassLikeCommand::class) ->args([ service(ClassLikeAnalyser::class), service(Loader::class), - ]) - ->public(); + ]); $services ->set(DebugLayerCommand::class) ->args([ service(LayerAnalyser::class), service(Loader::class), - ]) - ->public(); + ]); }; diff --git a/src/Console/Application.php b/src/Console/Application.php index e7c153bb2..1f6657c6c 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -4,20 +4,17 @@ namespace Qossmic\Deptrac\Console; +use Psr\Container\ContainerInterface; use Qossmic\Deptrac\Console\Command\AnalyzeCommand; use Qossmic\Deptrac\Console\Command\DebugClassLikeCommand; use Qossmic\Deptrac\Console\Command\DebugLayerCommand; use Qossmic\Deptrac\Console\Command\InitCommand; +use Qossmic\Deptrac\ContainerBuilder; use Qossmic\Deptrac\ShouldNotHappenException; -use Symfony\Component\Config\FileLocator; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; -use Symfony\Component\EventDispatcher\EventDispatcher; final class Application extends BaseApplication { @@ -59,28 +56,16 @@ public function doRun(InputInterface $input, OutputInterface $output): int return parent::doRun($input, $output); } - private function buildContainer(InputInterface $input, string $currentWorkingDirectory): ContainerBuilder + private function buildContainer(InputInterface $input, string $currentWorkingDirectory): ContainerInterface { - $container = new ContainerBuilder(); - $container->setParameter('currentWorkingDirectory', $currentWorkingDirectory); - $container->addCompilerPass( - new RegisterListenersPass(EventDispatcher::class, 'event_listener', 'event_subscriber') - ); - - $fileLoader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config/')); - $fileLoader->load('services.php'); + $containerBuilder = new ContainerBuilder($currentWorkingDirectory); if (false === $input->hasParameterOption('--no-cache')) { - $container->setParameter( - 'deptrac.cache_file', - $input->getParameterOption('--cache-file', getcwd().'/.deptrac.cache') - ); - - $fileLoader->load('cache.php'); + /** @var string $cacheFile */ + $cacheFile = $input->getParameterOption('--cache-file', $currentWorkingDirectory.'/.deptrac.cache'); + $containerBuilder->useCache($cacheFile); } - $container->compile(); - - return $container; + return $containerBuilder->build(); } } diff --git a/src/ContainerBuilder.php b/src/ContainerBuilder.php new file mode 100644 index 000000000..f0e4d102a --- /dev/null +++ b/src/ContainerBuilder.php @@ -0,0 +1,53 @@ +currentWorkingDirectory = $currentWorkingDirectory; + } + + public function useCache(string $cacheFile): self + { + $this->cacheFile = $cacheFile; + + return $this; + } + + public function build(): ContainerInterface + { + $container = new SymfonyContainerBuilder(); + $container->setParameter('currentWorkingDirectory', $this->currentWorkingDirectory); + $container->addCompilerPass( + new RegisterListenersPass(EventDispatcher::class, 'event_listener', 'event_subscriber') + ); + + $fileLoader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../config/')); + $fileLoader->load('services.php'); + + if (null !== $this->cacheFile) { + $container->setParameter('deptrac.cache_file', $this->cacheFile); + $fileLoader->load('cache.php'); + } + + $container->compile(); + + return $container; + } +} diff --git a/src/LayerAnalyser.php b/src/LayerAnalyser.php index 9b4fcfe4f..cbbd1f692 100644 --- a/src/LayerAnalyser.php +++ b/src/LayerAnalyser.php @@ -39,6 +39,14 @@ public function analyse(Configuration $configuration, string $layer): array /** @var string[] $classLikeNames */ $classLikeNames = []; + + foreach ($astMap->getAstClassReferences() as $classReference) { + $classLikeName = $classReference->getClassLikeName(); + if ($this->isInLayer($layer, $classLikeName, $classLikeLayerResolver)) { + $classLikeNames[] = $classLikeName->toString(); + } + } + foreach ($dependencyResult->getDependenciesAndInheritDependencies() as $dependency) { $classLikeName = $dependency->getClassLikeNameA(); if ($this->isInLayer($layer, $classLikeName, $classLikeLayerResolver)) { diff --git a/tests/ClassLikeAnalyserTest.php b/tests/ClassLikeAnalyserTest.php new file mode 100644 index 000000000..527cdaf54 --- /dev/null +++ b/tests/ClassLikeAnalyserTest.php @@ -0,0 +1,41 @@ + [__DIR__.'/Fixtures/ClassLikeAnalyser/'], + 'layers' => [ + [ + 'name' => 'LayerFoo', + 'collectors' => [ + [ + 'type' => 'className', + 'regex' => '.*ClassFoo', + ], + ], + ], + ], + 'ruleset' => [], + ]); + + $container = (new ContainerBuilder(__DIR__))->build(); + $analyser = $container->get(ClassLikeAnalyser::class); + $layers = $analyser->analyse($configuration, ClassLikeName::fromFQCN(ClassFoo::class)); + + self::assertSame(['LayerFoo'], $layers); + } +} diff --git a/tests/Fixtures/ClassLikeAnalyser/ClassFoo.php b/tests/Fixtures/ClassLikeAnalyser/ClassFoo.php new file mode 100644 index 000000000..55d2d40ad --- /dev/null +++ b/tests/Fixtures/ClassLikeAnalyser/ClassFoo.php @@ -0,0 +1,10 @@ + [__DIR__.'/Fixtures/LayerAnalyser/'], + 'layers' => [ + [ + 'name' => 'LayerFoo', + 'collectors' => [ + [ + 'type' => 'className', + 'regex' => '.*Class.*', + ], + ], + ], + ], + 'ruleset' => [], + ]); + + $container = (new ContainerBuilder(__DIR__))->build(); + $analyser = $container->get(LayerAnalyser::class); + $classLikes = $analyser->analyse($configuration, 'LayerFoo'); + + self::assertSame( + [ + ClassBar::class, + ClassFoo::class, + ], + $classLikes + ); + } +}