diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index c592f27..534a604 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -1,4 +1,5 @@ on: + pull_request: null push: branches: - master diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 75c416b..ebfebb3 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -1,4 +1,5 @@ on: + pull_request: null push: branches: - master diff --git a/composer.json b/composer.json index 31755e2..7a296d4 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,13 @@ ], "require": { "php": "^8.1", - "spiral/boot": "^3.0", - "spiral/core": "^3.0" + "spiral/boot": "^3.10", + "spiral/core": "^3.10" }, "require-dev": { - "spiral/framework": "^3.0", - "spiral/testing": "^2.0", - "vimeo/psalm": "^4.9" + "spiral/framework": "dev-feature/bootloaders-registry as 3.10.x-dev", + "spiral/testing": "^2.6", + "vimeo/psalm": "^5.15" }, "autoload": { "psr-4": { diff --git a/src/Bootloader/ArrayRegistry.php b/src/Bootloader/ArrayRegistry.php deleted file mode 100644 index 0878a4b..0000000 --- a/src/Bootloader/ArrayRegistry.php +++ /dev/null @@ -1,38 +0,0 @@ -> $bootloaders - * @param TClass[] $ignorableBootloaders - */ - public function __construct( - private readonly array $bootloaders, - private readonly array $ignorableBootloaders = [] - ) { - } - - public function init(ContainerInterface $container): void - { - } - - public function getBootloaders(): array - { - return $this->bootloaders; - } - - public function getIgnoredBootloaders(): array - { - return $this->ignorableBootloaders; - } -} diff --git a/src/Bootloader/BootloaderRegistryInterface.php b/src/Bootloader/BootloaderRegistryInterface.php index 1e6df14..a99684f 100644 --- a/src/Bootloader/BootloaderRegistryInterface.php +++ b/src/Bootloader/BootloaderRegistryInterface.php @@ -13,7 +13,7 @@ interface BootloaderRegistryInterface extends RegistryInterface { /** - * @return TClass[]|array> + * @return TClass[]|array> */ public function getBootloaders(): array; diff --git a/src/Bootloader/ConfigRegistry.php b/src/Bootloader/ConfigRegistry.php index 5c79546..8a0c25b 100644 --- a/src/Bootloader/ConfigRegistry.php +++ b/src/Bootloader/ConfigRegistry.php @@ -5,8 +5,12 @@ namespace Spiral\Discoverer\Bootloader; use Psr\Container\ContainerInterface; +use Spiral\Boot\BootloadManagerInterface; use Spiral\Discoverer\Config\DiscovererConfig; +/** + * @psalm-import-type TClass from BootloadManagerInterface + */ final class ConfigRegistry implements BootloaderRegistryInterface { private ?DiscovererConfig $config = null; @@ -21,6 +25,9 @@ public function init(ContainerInterface $container): void \assert($this->config instanceof DiscovererConfig); } + /** + * @return TClass[]|array> + */ public function getBootloaders(): array { return $this->config !== null ? $this->config->getBootloaders() : []; diff --git a/src/Config/DiscovererConfig.php b/src/Config/DiscovererConfig.php index 7db41dd..3e087b2 100644 --- a/src/Config/DiscovererConfig.php +++ b/src/Config/DiscovererConfig.php @@ -4,23 +4,44 @@ namespace Spiral\Discoverer\Config; -use Spiral\Boot\Bootloader\BootloaderInterface; use Spiral\Boot\BootloadManagerInterface; use Spiral\Core\InjectableConfig; +use Spiral\Discoverer\Bootloader\BootloaderRegistryInterface; +use Spiral\Discoverer\Bootloader as BootloaderRegistry; +use Spiral\Discoverer\Tokenizer as TokenizerRegistry; +use Spiral\Discoverer\Tokenizer\DirectoryRegistryInterface; /** * @psalm-import-type TClass from BootloadManagerInterface + * @property array{ + * bootloaders: array|array>, + * ignoredBootloaders: array, + * registries: array{ + * bootloaders: array>, + * directories: array> + * } + * } $config */ final class DiscovererConfig extends InjectableConfig { public const CONFIG = 'discoverer'; + protected array $config = [ 'bootloaders' => [], 'ignoredBootloaders' => [], + 'registries' => [ + 'bootloaders' => [ + BootloaderRegistry\ComposerRegistry::class, + BootloaderRegistry\ConfigRegistry::class, + ], + 'directories' => [ + TokenizerRegistry\ComposerRegistry::class, + ], + ], ]; /** - * @return TClass[]|array> + * @return array|array> */ public function getBootloaders(): array { @@ -28,10 +49,26 @@ public function getBootloaders(): array } /** - * @return TClass[] + * @return array */ public function getIgnoredBootloaders(): array { return (array)$this->config['ignoredBootloaders']; } + + /** + * @return array> + */ + public function getBootloaderRegistries(): array + { + return $this->config['registries']['bootloaders'] ?? []; + } + + /** + * @return array> + */ + public function getDirectoryRegistries(): array + { + return $this->config['registries']['directories'] ?? []; + } } diff --git a/src/DiscovererBootloader.php b/src/DiscovererBootloader.php index d1c92ed..d2ad974 100644 --- a/src/DiscovererBootloader.php +++ b/src/DiscovererBootloader.php @@ -4,37 +4,104 @@ namespace Spiral\Discoverer; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; use Spiral\Boot\Bootloader\Bootloader; -use Spiral\Discoverer\Tokenizer\DirectoriesDiscoverer; +use Spiral\Boot\Bootloader\BootloaderRegistryInterface; +use Spiral\Config\ConfiguratorInterface; +use Spiral\Discoverer\Bootloader as BootloaderRegistry; +use Spiral\Discoverer\Config\DiscovererConfig; +use Spiral\Discoverer\Tokenizer as TokenizerRegistry; use Spiral\Tokenizer\Bootloader\TokenizerBootloader; final class DiscovererBootloader extends Bootloader { - protected const DEPENDENCIES = [ - TokenizerBootloader::class, - ]; + public function __construct( + private readonly ConfiguratorInterface $config, + ) { + } + + public function defineSingletons(): array + { + return [ + DiscovererInterface::class => [self::class, 'initDiscoverer'], + ]; + } + + public function init(ContainerInterface $container, TokenizerBootloader $tokenizerBootloader): void + { + $this->initDefaultConfig(); + $this->registerDirectories($container, $tokenizerBootloader); + $this->registerBootloaders($container); + } + + private function initDefaultConfig(): void + { + $this->config->setDefaults(DiscovererConfig::CONFIG, [ + 'bootloaders' => [], + 'ignoredBootloaders' => [], + 'registries' => [ + 'bootloaders' => [ + BootloaderRegistry\ComposerRegistry::class, + BootloaderRegistry\ConfigRegistry::class, + ], + 'directories' => [ + TokenizerRegistry\ComposerRegistry::class, + ], + ], + ]); + } /** * @throws Exception\DiscovererRegistryException - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ - public function init( - ContainerInterface $container, - TokenizerBootloader $tokenizerBootloader - ): void { - if (!$container->has(DiscovererInterface::class)) { - return; + private function registerDirectories(ContainerInterface $container, TokenizerBootloader $tokenizerBootloader): void + { + $discoverer = $container->get(DiscovererInterface::class); + + if ($discoverer->has(TokenizerRegistry\DirectoriesDiscoverer::getName())) { + foreach ($discoverer->discover(TokenizerRegistry\DirectoriesDiscoverer::getName()) as $directory) { + $tokenizerBootloader->addDirectory($directory); + } } + } + private function registerBootloaders(ContainerInterface $container): void + { $discoverer = $container->get(DiscovererInterface::class); - \assert($discoverer instanceof DiscovererInterface); + $bootloadersRegistry = $container->get(BootloaderRegistryInterface::class); - if ($discoverer->has(DirectoriesDiscoverer::getName())) { - foreach ($discoverer->discover(DirectoriesDiscoverer::getName()) as $directory) { - $tokenizerBootloader->addDirectory($directory); + if ($discoverer->has(BootloaderRegistry\BootloadersDiscoverer::getName())) { + $bootloaders = $discoverer->discover(BootloaderRegistry\BootloadersDiscoverer::getName()); + foreach ($bootloaders as $bootloader => $options) { + $bootloadersRegistry->register([$bootloader => $options]); } - }; + } + } + + private function initDiscoverer(DiscovererConfig $config, ContainerInterface $container): DiscovererInterface + { + $bootloaders = []; + foreach ($config->getBootloaderRegistries() as $registry) { + $registry = $container->get($registry); + \assert($registry instanceof BootloaderRegistry\BootloaderRegistryInterface); + $bootloaders[] = $registry; + } + + $directories = []; + foreach ($config->getDirectoryRegistries() as $registry) { + $registry = $container->get($registry); + \assert($registry instanceof TokenizerRegistry\DirectoryRegistryInterface); + $directories[] = $registry; + } + + return new Discoverer( + $container, + new BootloaderRegistry\BootloadersDiscoverer(...$bootloaders), + new TokenizerRegistry\DirectoriesDiscoverer(...$directories), + ); } } diff --git a/src/WithDiscovering.php b/src/WithDiscovering.php deleted file mode 100644 index a4f05a5..0000000 --- a/src/WithDiscovering.php +++ /dev/null @@ -1,31 +0,0 @@ -discoverer = new Discoverer($this->container, ...$registry); - - $this->container->bindSingleton( - DiscovererInterface::class, - $this->discoverer - ); - } - - protected function defineBootloaders(): array - { - if ($this->discoverer === null) { - return parent::defineBootloaders(); - } - - return parent::defineBootloaders() + $this->discoverer->discover(BootloadersDiscoverer::getName()); - } -} diff --git a/tests/src/Bootloader/ArrayRegistryTest.php b/tests/src/Bootloader/ArrayRegistryTest.php deleted file mode 100644 index f85a37f..0000000 --- a/tests/src/Bootloader/ArrayRegistryTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertSame($bootloaders, $registry->getBootloaders()); - } - - public function testGetsIgnorableBootloaders(): void - { - $registry = new ArrayRegistry([], $bootloaders = [ - 'BootloaderA', - 'BootloaderB', - ]); - - $this->assertSame($bootloaders, $registry->getIgnoredBootloaders()); - } -} diff --git a/tests/src/Bootloader/BootloadersDiscovererTest.php b/tests/src/Bootloader/BootloadersDiscovererTest.php index a990511..acfa9d4 100644 --- a/tests/src/Bootloader/BootloadersDiscovererTest.php +++ b/tests/src/Bootloader/BootloadersDiscovererTest.php @@ -10,12 +10,9 @@ final class BootloadersDiscovererTest extends TestCase { - public function testGetsName() + public function testGetsName(): void { - return $this->assertSame( - 'bootloaders', - BootloadersDiscoverer::getName() - ); + $this->assertSame('bootloaders', BootloadersDiscoverer::getName()); } public function testDiscover(): void diff --git a/tests/src/DiscovererBootloaderTest.php b/tests/src/DiscovererBootloaderTest.php new file mode 100644 index 0000000..26065e5 --- /dev/null +++ b/tests/src/DiscovererBootloaderTest.php @@ -0,0 +1,18 @@ +getConfig('tokenizer')['directories']; + + $rootDir = $this->getDirectoryByAlias('root'); + + $this->assertContains($rootDir.'vendor/composer/../foo/notifications/src/foo', $dirs); + $this->assertContains($rootDir.'vendor/composer/../foo/notifications/src/bar', $dirs); + } +} diff --git a/tests/src/DiscovererBootloaderWithDiscovererTest.php b/tests/src/DiscovererBootloaderWithDiscovererTest.php deleted file mode 100644 index 1b526c5..0000000 --- a/tests/src/DiscovererBootloaderWithDiscovererTest.php +++ /dev/null @@ -1,39 +0,0 @@ -discoverer = m::mock(DiscovererInterface::class); - $this->beforeInit(function (Container $container) { - $container->bindSingleton(DiscovererInterface::class, $this->discoverer); - }); - - $this->discoverer->shouldReceive('has')->with(DirectoriesDiscoverer::getName())->andReturnTrue(); - $this->discoverer->shouldReceive('discover')->with(DirectoriesDiscoverer::getName())->andReturn([ - 'src/foo', - 'src/bar', - ]); - - parent::setUp(); - } - - public function testTokenizerDirectoriesShouldBeDiscovered() - { - $dirs = $this->getConfig('tokenizer')['directories']; - - $this->assertContains('src/foo', $dirs); - $this->assertContains('src/bar', $dirs); - } -} diff --git a/tests/src/DiscovererTest.php b/tests/src/DiscovererTest.php index 3976dec..a6d06c3 100644 --- a/tests/src/DiscovererTest.php +++ b/tests/src/DiscovererTest.php @@ -46,7 +46,7 @@ public function init(ContainerInterface $container): void public function testNonExistDiscovererShouldThrowAnException() { $this->expectException(DiscovererRegistryException::class); - $this->expectErrorMessage('Registry with name [test] does not exist.'); + $this->expectExceptionMessage('Registry with name [test] does not exist.'); $discoverer = new Discoverer($this->getContainer()); $discoverer->discover('test');