diff --git a/config/service_manager.php b/config/service_manager.php index c6f47ba8..5886b570 100644 --- a/config/service_manager.php +++ b/config/service_manager.php @@ -3,8 +3,10 @@ declare(strict_types=1); use Icanhazstring\Composer\Unused\Command\Handler\CollectConsumedSymbolsCommandHandler; +use Icanhazstring\Composer\Unused\Command\Handler\CollectFilteredDependenciesCommandHandler; use Icanhazstring\Composer\Unused\Command\Handler\CollectRequiredDependenciesCommandHandler; use Icanhazstring\Composer\Unused\Command\Handler\Factory\CollectConsumedSymbolsCommandHandlerFactory; +use Icanhazstring\Composer\Unused\Command\Handler\Factory\CollectFilteredDependenciesCommandHandlerFactory; use Icanhazstring\Composer\Unused\Command\Handler\Factory\CollectRequiredDependenciesCommandHandlerFactory; use Icanhazstring\Composer\Unused\Console\Command\UnusedCommand; use Icanhazstring\Composer\Unused\Console\Command\UnusedCommandFactory; @@ -18,6 +20,7 @@ UnusedCommand::class => UnusedCommandFactory::class, CollectConsumedSymbolsCommandHandler::class => CollectConsumedSymbolsCommandHandlerFactory::class, CollectRequiredDependenciesCommandHandler::class => CollectRequiredDependenciesCommandHandlerFactory::class, + CollectFilteredDependenciesCommandHandler::class => CollectFilteredDependenciesCommandHandlerFactory::class, ConsumedSymbolLoaderBuilder::class => InvokableFactory::class, ProvidedSymbolLoaderBuilder::class => InvokableFactory::class, PackageResolver::class => InvokableFactory::class, diff --git a/src/Command/FilterDependencyCollectionCommand.php b/src/Command/FilterDependencyCollectionCommand.php new file mode 100644 index 00000000..8b6b1647 --- /dev/null +++ b/src/Command/FilterDependencyCollectionCommand.php @@ -0,0 +1,44 @@ + */ + private $namedExclusion; + + /** + * @param DependencyCollection $requiredDependencyCollection + * @param array $namedExclusion + */ + public function __construct(DependencyCollection $requiredDependencyCollection, array $namedExclusion) + { + $this->requiredDependencyCollection = $requiredDependencyCollection; + $this->namedExclusion = array_merge(self::GLOBAL_EXCLUSION, $namedExclusion); + } + + public function getRequiredDependencyCollection(): DependencyCollection + { + return $this->requiredDependencyCollection; + } + + /** + * @return array + */ + public function getNamedExclusion(): array + { + return $this->namedExclusion; + } +} diff --git a/src/Command/Handler/CollectFilteredDependenciesCommandHandler.php b/src/Command/Handler/CollectFilteredDependenciesCommandHandler.php new file mode 100644 index 00000000..7d4a00da --- /dev/null +++ b/src/Command/Handler/CollectFilteredDependenciesCommandHandler.php @@ -0,0 +1,20 @@ +getNamedExclusion(); + + return $command->getRequiredDependencyCollection()->filter(static function ($dependency) use ($namedExclusion) { + return !in_array($dependency->getName(), $namedExclusion); + }); + } +} diff --git a/src/Command/Handler/Factory/CollectFilteredDependenciesCommandHandlerFactory.php b/src/Command/Handler/Factory/CollectFilteredDependenciesCommandHandlerFactory.php new file mode 100644 index 00000000..b0c7739d --- /dev/null +++ b/src/Command/Handler/Factory/CollectFilteredDependenciesCommandHandlerFactory.php @@ -0,0 +1,16 @@ +collectConsumedSymbolsCommandHandler = $collectConsumedSymbolsCommandHandler; $this->collectRequiredDependenciesCommandHandler = $collectRequiredDependenciesCommandHandler; + $this->collectFilteredDependenciesCommandHandler = $collectFilteredDependenciesCommandHandler; } protected function configure(): void @@ -95,7 +101,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ) ); - $requiredDependencyCollection = $this->collectRequiredDependenciesCommandHandler->collect( + $unfilteredRequiredDependencyCollection = $this->collectRequiredDependenciesCommandHandler->collect( new LoadRequiredDependenciesCommand( $baseDir . DIRECTORY_SEPARATOR . $composer->getConfig()->get('vendor-dir'), $rootPackage->getRequires(), @@ -103,6 +109,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int ) ); + $requiredDependencyCollection = $this->collectFilteredDependenciesCommandHandler->collect( + new FilterDependencyCollectionCommand( + $unfilteredRequiredDependencyCollection, + $input->getOption('excludePackage') + ) + ); + $io = new SymfonyStyle($input, $output); foreach ($consumedSymbols as $symbol) { diff --git a/src/Console/Command/UnusedCommandFactory.php b/src/Console/Command/UnusedCommandFactory.php index e51a2982..8bb56122 100644 --- a/src/Console/Command/UnusedCommandFactory.php +++ b/src/Console/Command/UnusedCommandFactory.php @@ -5,6 +5,7 @@ namespace Icanhazstring\Composer\Unused\Console\Command; use Icanhazstring\Composer\Unused\Command\Handler\CollectConsumedSymbolsCommandHandler; +use Icanhazstring\Composer\Unused\Command\Handler\CollectFilteredDependenciesCommandHandler; use Icanhazstring\Composer\Unused\Command\Handler\CollectRequiredDependenciesCommandHandler; use Psr\Container\ContainerInterface; @@ -14,7 +15,8 @@ public function __invoke(ContainerInterface $container): UnusedCommand { return new UnusedCommand( $container->get(CollectConsumedSymbolsCommandHandler::class), - $container->get(CollectRequiredDependenciesCommandHandler::class) + $container->get(CollectRequiredDependenciesCommandHandler::class), + $container->get(CollectFilteredDependenciesCommandHandler::class), ); } } diff --git a/src/Dependency/DependencyCollection.php b/src/Dependency/DependencyCollection.php index db989387..da3a78ac 100644 --- a/src/Dependency/DependencyCollection.php +++ b/src/Dependency/DependencyCollection.php @@ -65,4 +65,15 @@ public function partition(Closure $partition): array new self($noMatches) ]; } + + /** + * @param Closure $fn + * @return DependencyCollection + */ + public function filter(Closure $fn): DependencyCollection + { + return new self( + array_filter($this->items, $fn) + ); + } } diff --git a/tests/Integration/UnusedCommandTest.php b/tests/Integration/UnusedCommandTest.php index 9ece7940..7c6f7d92 100644 --- a/tests/Integration/UnusedCommandTest.php +++ b/tests/Integration/UnusedCommandTest.php @@ -12,6 +12,7 @@ use Icanhazstring\Composer\Unused\Di\ServiceContainer; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\NullOutput; class UnusedCommandTest extends TestCase @@ -103,4 +104,38 @@ public function itShouldNoReportUnusedWithAutoloadFilesWithRequire(): void ) ); } + + /** + * @test + */ + public function itShouldNotReportSpecialPackages(): void + { + chdir(__DIR__ . '/../assets/TestProjects/IgnoreSpecialPackages'); + + $output = new BufferedOutput(); + + $this->getApplication()->run( + new ArrayInput(['unused']), + $output + ); + + self::assertStringNotContainsString('composer-plugin-api', $output->fetch()); + } + + /** + * @test + */ + public function itShouldNotReportExcludedPackages(): void + { + chdir(__DIR__ . '/../assets/TestProjects/IgnoreExcludedPackages'); + + $output = new BufferedOutput(); + + $this->getApplication()->run( + new ArrayInput(['unused', '--excludePackage' => ['dummy/test-package']]), + $output + ); + + self::assertStringNotContainsString('dummy/test-package', $output->fetch()); + } } diff --git a/tests/assets/TestProjects/IgnoreExcludedPackages/composer.json b/tests/assets/TestProjects/IgnoreExcludedPackages/composer.json new file mode 100644 index 00000000..233f7a0b --- /dev/null +++ b/tests/assets/TestProjects/IgnoreExcludedPackages/composer.json @@ -0,0 +1,7 @@ +{ + "name": "ns/lib", + "require": { + "dummy/test-package": "1.0", + "composer-plugin-api": "^2.0" + } +} diff --git a/tests/assets/TestProjects/IgnoreSpecialPackages/composer.json b/tests/assets/TestProjects/IgnoreSpecialPackages/composer.json new file mode 100644 index 00000000..71cd6cdb --- /dev/null +++ b/tests/assets/TestProjects/IgnoreSpecialPackages/composer.json @@ -0,0 +1,6 @@ +{ + "name": "ns/lib", + "require": { + "composer-plugin-api": "^2.0" + } +}