From e8e120d37a367e6c5897bc4278373fcee1afdc5f Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 11 Feb 2022 15:53:47 +0300 Subject: [PATCH] Package refactoring - Added ability to use custom Discoverers - Added tokenizer discoverer - Covered with unit tests --- README.md | 248 ++++++++++++++---- composer.json | 25 +- .../ArrayRegistry.php | 7 +- .../BootloaderRegistryInterface.php | 20 ++ src/Bootloader/BootloadersDiscoverer.php | 83 ++++++ src/Bootloader/ComposerRegistry.php | 51 ++++ src/Bootloader/ConfigRegistry.php | 28 ++ src/Composer.php | 84 ++++++ src/Config/BootloadersConfig.php | 26 -- src/Config/DiscovererConfig.php | 26 ++ src/Discoverer.php | 68 ++--- src/DiscovererBootloader.php | 31 +++ src/DiscovererInterface.php | 17 ++ src/DiscovererRegistryInterface.php | 11 + src/Exception/DiscovererException.php | 10 + src/Exception/DiscovererRegistryException.php | 10 + src/Registry/ComposerRegistry.php | 88 ------- src/Registry/ConfigRegistry.php | 29 -- src/RegistryInterface.php | 12 +- src/Tokenizer/ComposerRegistry.php | 40 +++ src/Tokenizer/DirectoriesDiscoverer.php | 44 ++++ src/Tokenizer/DirectoryRegistryInterface.php | 12 + src/Traits/WithComposerPackages.php | 30 +++ src/WithBootloadersDiscovering.php | 24 -- src/WithDiscovering.php | 31 +++ tests/composer.json | 13 +- .../ArrayRegistryTest.php | 8 +- .../Bootloader/BootloadersDiscovererTest.php | 56 ++++ .../ComposerRegistryTest.php | 8 +- .../ConfigRegistryTest.php | 20 +- ...DiscovererBootloaderWithDiscovererTest.php | 39 +++ tests/src/DiscovererTest.php | 69 ++--- tests/src/TestCase.php | 6 +- tests/src/Tokenizer/ComposerRegistryTest.php | 34 +++ .../Tokenizer/DirectoriesDiscovererTest.php | 51 ++++ 35 files changed, 1021 insertions(+), 338 deletions(-) rename src/{Registry => Bootloader}/ArrayRegistry.php (73%) create mode 100644 src/Bootloader/BootloaderRegistryInterface.php create mode 100644 src/Bootloader/BootloadersDiscoverer.php create mode 100644 src/Bootloader/ComposerRegistry.php create mode 100644 src/Bootloader/ConfigRegistry.php create mode 100644 src/Composer.php delete mode 100644 src/Config/BootloadersConfig.php create mode 100644 src/Config/DiscovererConfig.php create mode 100644 src/DiscovererBootloader.php create mode 100644 src/DiscovererInterface.php create mode 100644 src/DiscovererRegistryInterface.php create mode 100644 src/Exception/DiscovererException.php create mode 100644 src/Exception/DiscovererRegistryException.php delete mode 100644 src/Registry/ComposerRegistry.php delete mode 100644 src/Registry/ConfigRegistry.php create mode 100644 src/Tokenizer/ComposerRegistry.php create mode 100644 src/Tokenizer/DirectoriesDiscoverer.php create mode 100644 src/Tokenizer/DirectoryRegistryInterface.php create mode 100644 src/Traits/WithComposerPackages.php delete mode 100644 src/WithBootloadersDiscovering.php create mode 100644 src/WithDiscovering.php rename tests/src/{Registry => Bootloader}/ArrayRegistryTest.php (69%) create mode 100644 tests/src/Bootloader/BootloadersDiscovererTest.php rename tests/src/{Registry => Bootloader}/ComposerRegistryTest.php (82%) rename tests/src/{Registry => Bootloader}/ConfigRegistryTest.php (58%) create mode 100644 tests/src/DiscovererBootloaderWithDiscovererTest.php create mode 100644 tests/src/Tokenizer/ComposerRegistryTest.php create mode 100644 tests/src/Tokenizer/DirectoriesDiscovererTest.php diff --git a/README.md b/README.md index b5f6413..7b1179e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Bootloaders discoverer for Spiral Framework +# Discoverer for Spiral Framework -[![Latest Version on Packagist](https://img.shields.io/packagist/v/spiral-packages/bootloaders-discover.svg?style=flat-square)](https://packagist.org/packages/spiral-packages/bootloaders-discover) -[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/spiral-packages/bootloaders-discover/run-tests?label=tests)](https://github.com/spiral-packages/bootloaders-discover/actions?query=workflow%3Arun-tests+branch%3Amain) -[![Total Downloads](https://img.shields.io/packagist/dt/spiral-packages/bootloaders-discover.svg?style=flat-square)](https://packagist.org/packages/spiral-packages/bootloaders-discover) +[![Latest Version on Packagist](https://img.shields.io/packagist/v/spiral-packages/discoverer.svg?style=flat-square)](https://packagist.org/packages/spiral-packages/discoverer) +[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/spiral-packages/discoverer/run-tests?label=tests)](https://github.com/spiral-packages/discoverer/actions?query=workflow%3Arun-tests+branch%3Amain) +[![Total Downloads](https://img.shields.io/packagist/dt/spiral-packages/discoverer.svg?style=flat-square)](https://packagist.org/packages/spiral-packages/discoverer) ## Requirements @@ -16,23 +16,32 @@ Make sure that your server is configured with following PHP version and extensio You can install the package via composer: ```bash -composer require spiral-packages/bootloaders-discover +composer require spiral-packages/discoverer ``` -After package install you need to add `Spiral\BootloadersDiscover\WithBootloadersDiscovering` trait from the package to -your Application kernel. +After package install you need to register bootloader from the package. ```php +protected const LOAD = [ + // ... + \Spiral\Discoverer\DiscovererBootloader::class, +]; +``` + +After package install you need to add `Spiral\Discoverer\WithDiscovering` trait from the package to your Application +kernel. + +```php +use Spiral\Discoverer\WithDiscovering; use Spiral\Framework\Kernel; -use Spiral\BootloadersDiscover\WithBootloadersDiscovering; class App extends Kernel { - use WithBootloadersDiscovering; + use WithDiscovering; } ``` -And then you should modify you `app.php` file. +And then you should modify you `app.php` file. Example you can see below. ```php __DIR__ ]); -$app->discoverBootloadersFrom( - // Will load bootloaders from composer.json and from other installed composer packages - // "extra": { - // "spiral": { - // "bootloaders": [ - // "Spiral\\Monolog\\Bootloader\\DotenvBootloader", - // "Spiral\\DotEnv\\Bootloader\\MonologBootloader", - // ], - // "dont-discover": [ - // "spiral-packages/event-bus" - // ] - // } - // }, - new \Spiral\BootloadersDiscover\Registry\ComposerRegistry(), +$app->discover( + new \Spiral\Discoverer\Bootloader\BootloadersDiscoverer( + new \Spiral\Discoverer\Bootloader\ComposerRegistry(), + new \Spiral\Discoverer\Bootloader\ArrayRegistry(...), + new \Spiral\Discoverer\Bootloader\ConfigRegistry() + ), - // Will load bootloaders from passed array of bootloaders - new \Spiral\BootloadersDiscover\Registry\ArrayRegistry([ - // Application specific logs - Bootloader\LoggingBootloader::class, - - // ... - ]), - - // Will load bootloaders from config/bootloaders.php - new \Spiral\BootloadersDiscover\Registry\ConfigRegistry() + new \Spiral\Discoverer\Tokenizer\DirectoriesDiscoverer( + new \Spiral\Discoverer\Tokenizer\ComposerRegistry(), + ) ); if ($app->run() !== null) { @@ -97,9 +91,74 @@ if ($app->run() !== null) { } ``` -### Example of config/bootloaders.php +### Bootloaders discoverer + +It will help you to register bootloaders from different sources + +#### From composer + +Will register bootloaders from application `composer.json` and from other installed composer packages + +```php +new \Spiral\Discoverer\Bootloader\ComposerRegistry(), +``` + +**Package composer.json** + +```json +{ + // ... + "extra": { + "spiral": { + "bootloaders": [ + "Spiral\\Monolog\\Bootloader\\DotenvBootloader", + "Spiral\\DotEnv\\Bootloader\\MonologBootloader" + ], + "dont-discover": [ + "spiral-packages/event-bus" + ] + } + } +} +``` + +**Application composer.json** + +```json +{ + // ... + "extra": { + "spiral": { + "dont-discover": [ + "spiral-packages/foo", + "spiral-packages/bar" + ] + } + } +} +``` + +#### From array + +Will register bootloaders from the passed array ```php +new \Spiral\Discoverer\Bootloader\ArrayRegistry([ + // Application specific logs + Bootloader\LoggingBootloader::class, + + // ... +]), +``` + +#### From config + +```php +new \Spiral\Discoverer\Bootloader\ConfigRegistry(), +``` + +```php +// config/discoverer.php [ + 'ignoredBootloaders' => [ // ... ], ]; ``` -### Custom registry +#### Custom Bootloader registry -You have the ability to create your custom Registries by implementing `Spiral\BootloadersDiscover\RegistryInterface` +You have the ability to create your custom Registries by +implementing `Spiral\Discoverer\Bootloader\BootloaderRegistryInterface` ```php -use Spiral\BootloadersDiscover\RegistryInterface; +use Spiral\Discoverer\Bootloader\BootloaderRegistryInterface; use Spiral\Core\Container; use Spiral\Files\FilesInterface; -final class JsonRegistry implements RegistryInterface +final class JsonRegistry implements BootloaderRegistryInterface { private array $bootloaders = []; private array $ignorableBootloaders = []; @@ -144,13 +204,19 @@ final class JsonRegistry implements RegistryInterface public function init(Container $container): void { // json structure - // {"bootloaders": [], "ignorable_bootloaders": []} + // { + // "bootloaders": [ + // "Framework\Security\EncrypterBootloader", + // "Framework\Security\GuardBootloader" + // ], + // "ignored_bootloaders": [] + //} $files = $container->get(FilesInterface::class); $data = \json_decode($files->read($this->jsonPath), true); $this->bootloaders = $data['bootloaders'] ?? []; - $this->ignorableBootloaders = $data['ignorable_bootloaders'] ?? []; + $this->ignorableBootloaders = $data['ignored_bootloaders'] ?? []; } public function getBootloaders(): array @@ -158,13 +224,107 @@ final class JsonRegistry implements RegistryInterface return $this->bootloaders; } - public function getIgnorableBootloaders(): array + public function getIgnoredBootloaders(): array { return $this->ignorableBootloaders; } } ``` +### Tokenizer directories discoverer + +It will help you to register Tokenizer directories from different sources + +#### From composer + +Will register directories from application `composer.json` and from other installed composer packages + +**Package composer.json** + +```json +{ + // ... + "extra": { + "spiral": { + "directories": [ + "src/Entities" + ] + } + } +} +``` + +**Application composer.json** + +```json +{ + // ... + "extra": { + "spiral": { + "directories": { + "self": [ + "src/Events" + ], + "spiral-package/event-bus": [ + "src/Events" + ], + "spiral-package/notifications": "src/Events" + } + } + } +} +``` + +```php +new \Spiral\Discoverer\Tokenizer\ComposerRegistry(), +``` + +#### Custom Directory registry + +You have the ability to create your custom Registries by +implementing `Spiral\Discoverer\Tokenizer\DirectoryRegistryInterface` + +```php +use Spiral\Discoverer\Tokenizer\DirectoryRegistryInterface; +use Spiral\Core\Container; +use Spiral\Files\FilesInterface; + +final class JsonRegistry implements DirectoryRegistryInterface +{ + private array $directories = []; + + public function __construct( + private string $jsonPath + ) { + } + + public function init(Container $container): void + { + // json structure + // { + // "directories": [ + // "src/Listeners", + // "src/Entities" + // ] + // } + + $root = $container->get(\Spiral\Boot\DirectoriesInterface::class)->get('root'); + + $files = $container->get(FilesInterface::class); + $data = \json_decode($files->read($this->jsonPath), true); + + $this->directories = \array_map(function (string $dir) use($root) { + return $root . $dir; + }, $data['directories'] ?? []); + } + + public function getDirectories(): array + { + return $this->directories; + } +} +``` + ## Testing ```bash diff --git a/composer.json b/composer.json index 4bec98f..60f2e9f 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,10 @@ { "name": "spiral-packages/discoverer", - "description": "Bootloaders discoverer for Spiral Framework", + "description": "Discoverer for Spiral Framework", "keywords": [ "spiral-packages", "spiral", - "bootloaders-discover" + "discoverer" ], "homepage": "https://github.com/spiral-packages/bootloaders-discover", "license": "MIT", @@ -17,8 +17,7 @@ ], "require": { "php": "^8.0", - "spiral/boot": "^2.9", - "spiral/files": "^2.9" + "spiral/framework": "^2.9" }, "require-dev": { "mockery/mockery": "^1.5", @@ -28,13 +27,20 @@ }, "autoload": { "psr-4": { - "Spiral\\BootloadersDiscover\\": "src" + "Spiral\\Discoverer\\": "src" } }, "autoload-dev": { "psr-4": { - "Spiral\\BootloadersDiscover\\Tests\\App\\": "tests/app", - "Spiral\\BootloadersDiscover\\Tests\\": "tests/src" + "Spiral\\Discoverer\\Tests\\App\\": "tests/app", + "Spiral\\Discoverer\\Tests\\": "tests/src" + } + }, + "extra": { + "spiral": { + "bootloaders": [ + "Spiral\\Discoverer\\DiscovererBootloader" + ] } }, "scripts": { @@ -42,7 +48,10 @@ "psalm": "vendor/bin/psalm --config=psalm.xml ./src" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "spiral/composer-publish-plugin": true + } }, "minimum-stability": "dev", "prefer-stable": true diff --git a/src/Registry/ArrayRegistry.php b/src/Bootloader/ArrayRegistry.php similarity index 73% rename from src/Registry/ArrayRegistry.php rename to src/Bootloader/ArrayRegistry.php index c1c4aaa..bc136a2 100644 --- a/src/Registry/ArrayRegistry.php +++ b/src/Bootloader/ArrayRegistry.php @@ -2,12 +2,11 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover\Registry; +namespace Spiral\Discoverer\Bootloader; -use Spiral\BootloadersDiscover\RegistryInterface; use Spiral\Core\Container; -final class ArrayRegistry implements RegistryInterface +final class ArrayRegistry implements BootloaderRegistryInterface { /** * @param array> $bootloaders @@ -28,7 +27,7 @@ public function getBootloaders(): array return $this->bootloaders; } - public function getIgnorableBootloaders(): array + public function getIgnoredBootloaders(): array { return $this->ignorableBootloaders; } diff --git a/src/Bootloader/BootloaderRegistryInterface.php b/src/Bootloader/BootloaderRegistryInterface.php new file mode 100644 index 0000000..118b3f3 --- /dev/null +++ b/src/Bootloader/BootloaderRegistryInterface.php @@ -0,0 +1,20 @@ +|array> + */ + public function getBootloaders(): array; + + /** + * @return array + */ + public function getIgnoredBootloaders(): array; +} diff --git a/src/Bootloader/BootloadersDiscoverer.php b/src/Bootloader/BootloadersDiscoverer.php new file mode 100644 index 0000000..3b99663 --- /dev/null +++ b/src/Bootloader/BootloadersDiscoverer.php @@ -0,0 +1,83 @@ +registries = $registry; + } + + public static function getName(): string + { + return 'bootloaders'; + } + + public function discover(): array + { + $bootloaders = []; + $ignorableBootloaders = $this->getIgnoredBootloaders(); + + foreach ($this->registries as $registry) { + if (! $registry instanceof BootloaderRegistryInterface) { + continue; + } + + $registryBootloaders = $registry->getBootloaders(); + + foreach ($registryBootloaders as $class => $options) { + if (\is_string($options)) { + $class = $options; + $options = []; + } + + if (isset($bootloaders[$class]) || \in_array($class, $ignorableBootloaders)) { + continue; + } + + $bootloaders[$class] = $options; + } + } + + return $bootloaders; + } + + /** + * @return array + */ + private function getIgnoredBootloaders(): array + { + /** @var array $ignorableBootloaders */ + $ignorableBootloaders = []; + foreach ($this->registries as $registry) { + if (! $registry instanceof BootloaderRegistryInterface) { + continue; + } + + $ignorableBootloaders = \array_merge( + $ignorableBootloaders, + $registry->getIgnoredBootloaders() + ); + } + + return \array_unique($ignorableBootloaders); + } + + public function init(Container $container): void + { + foreach ($this->registries as $registry) { + $registry->init($container); + } + } +} diff --git a/src/Bootloader/ComposerRegistry.php b/src/Bootloader/ComposerRegistry.php new file mode 100644 index 0000000..5e94865 --- /dev/null +++ b/src/Bootloader/ComposerRegistry.php @@ -0,0 +1,51 @@ +getComposer()->getPackages() as $extra) { + $bootloaders = \array_merge( + $bootloaders, + (array)($extra['bootloaders'] ?? []) + ); + } + + return \array_unique($bootloaders); + } + + public function getIgnoredBootloaders(): array + { + $ignore = $this->getComposer()->getComposerExtra('dont-discover', []); + + foreach ($this->getComposer()->getPackages() as $extra) { + $ignore = array_merge( + $ignore, + (array)($extra['dont-discover'] ?? []) + ); + } + + $bootloaders = []; + + foreach ($ignore as $packageName) { + if ($package = $this->getComposer()->getPackageExtra($packageName)) { + $bootloaders = \array_merge( + $bootloaders, + (array)($package['bootloaders'] ?? []) + ); + } + } + + return \array_unique($bootloaders); + } +} diff --git a/src/Bootloader/ConfigRegistry.php b/src/Bootloader/ConfigRegistry.php new file mode 100644 index 0000000..ab1569a --- /dev/null +++ b/src/Bootloader/ConfigRegistry.php @@ -0,0 +1,28 @@ +config = $container->get(DiscovererConfig::class); + } + + public function getBootloaders(): array + { + return $this->config->getBootloaders(); + } + + public function getIgnoredBootloaders(): array + { + return $this->config->getIgnoredBootloaders(); + } +} diff --git a/src/Composer.php b/src/Composer.php new file mode 100644 index 0000000..f6e26d5 --- /dev/null +++ b/src/Composer.php @@ -0,0 +1,84 @@ +vendorDir = rtrim($rootDir, '\/').'/vendor'; + + if ($files->exists($path = $this->vendorDir.'/composer/installed.json')) { + $installed = \json_decode($files->read($path), true); + $packages = $installed['packages'] ?? $installed; + + foreach ($packages as $package) { + $packageName = $this->formatPackageName($package['name']); + $this->packageDirs[$packageName] = $this->vendorDir.'/composer/'.$package['install-path']; + + if (! isset($package['extra'][$extraKey])) { + continue; + } + + $this->packages[$packageName] = (array)$package['extra'][$extraKey]; + } + } + + if ($files->isFile($composerPath = $this->rootDir.'/composer.json')) { + $data = \json_decode( + file_get_contents($composerPath), + true + ); + + $this->composerExtra = (array)($data['extra'][$extraKey] ?? []); + } + } + + public function getComposerExtra(string $key, $default = null) + { + return $this->composerExtra[$key] ?? $default; + } + + public function getPackagePath(string $package): ?string + { + return $this->packageDirs[$package] ?? null; + } + + public function packageHasExtra(string $package, string $key): bool + { + return isset($this->packages[$package][$key]); + } + + public function getPackageExtra(string $package, array $default = null): ?array + { + if (! isset($this->packages[$package])) { + return $default; + } + + return (array)$this->packages[$package]; + } + + private function formatPackageName(string $name): string + { + return str_replace($this->vendorDir.'/', '', $name); + } + + public function getPackages(): array + { + return $this->packages; + } + + public function getRootDir(): string + { + return $this->rootDir; + } +} diff --git a/src/Config/BootloadersConfig.php b/src/Config/BootloadersConfig.php deleted file mode 100644 index d9f97e5..0000000 --- a/src/Config/BootloadersConfig.php +++ /dev/null @@ -1,26 +0,0 @@ - [], - 'ignorableBootloaders' => [], - ]; - - public function getBootloaders(): array - { - return (array)($this->config['bootloaders'] ?? []); - } - - public function getIgnorableBootloaders(): array - { - return (array)($this->config['ignorableBootloaders'] ?? []); - } -} diff --git a/src/Config/DiscovererConfig.php b/src/Config/DiscovererConfig.php new file mode 100644 index 0000000..da92703 --- /dev/null +++ b/src/Config/DiscovererConfig.php @@ -0,0 +1,26 @@ + [], + 'ignoredBootloaders' => [], + ]; + + public function getBootloaders(): array + { + return (array)($this->config['bootloaders'] ?? []); + } + + public function getIgnoredBootloaders(): array + { + return (array)($this->config['ignoredBootloaders'] ?? []); + } +} diff --git a/src/Discoverer.php b/src/Discoverer.php index f29d7d5..d84ebfe 100644 --- a/src/Discoverer.php +++ b/src/Discoverer.php @@ -1,70 +1,36 @@ registries = $registries; - } - - /** - * @return array> - */ - public function discover(Container $container): array - { - $this->initRegistries($container); - - $bootloaders = []; - $ignorableBootloaders = $this->getIgnorableBootloaders(); - - foreach ($this->registries as $registry) { - $registryBootloaders = $registry->getBootloaders(); - - foreach ($registryBootloaders as $class => $options) { - if (\is_string($options)) { - $class = $options; - $options = []; - } - - if (isset($bootloaders[$class]) || \in_array($class, $ignorableBootloaders)) { - continue; - } - - $bootloaders[$class] = $options; - } + public function __construct( + private Container $container, + DiscovererRegistryInterface ...$registries + ) { + foreach ($registries as $registry) { + $this->registries[$registry::getName()] = $registry; + $registry->init($container); } - - return $bootloaders; } - /** - * @return array - */ - protected function getIgnorableBootloaders(): array + public function discover(string $name): array { - /** @var array $ignorableBootloaders */ - $ignorableBootloaders = []; - foreach ($this->registries as $registry) { - $ignorableBootloaders = \array_merge( - $ignorableBootloaders, - $registry->getIgnorableBootloaders() - ); + if ($this->has($name)) { + return $this->registries[$name]->discover(); } - return \array_unique($ignorableBootloaders); + throw new DiscovererRegistryException(\sprintf('Registry with name [%s] does not exist.', $name)); } - protected function initRegistries(Container $container): void + public function has(string $name): bool { - foreach ($this->registries as $registry) { - $registry->init($container); - } + return isset($this->registries[$name]); } } diff --git a/src/DiscovererBootloader.php b/src/DiscovererBootloader.php new file mode 100644 index 0000000..c21e2c5 --- /dev/null +++ b/src/DiscovererBootloader.php @@ -0,0 +1,31 @@ +has(DiscovererInterface::class)) { + $discoverer = $container->get(DiscovererInterface::class); + if ($discoverer->has(DirectoriesDiscoverer::getName())) { + foreach ($discoverer->discover(DirectoriesDiscoverer::getName()) as $directory) { + $tokenizerBootloader->addDirectory($directory); + } + }; + } + } +} diff --git a/src/DiscovererInterface.php b/src/DiscovererInterface.php new file mode 100644 index 0000000..fef2c15 --- /dev/null +++ b/src/DiscovererInterface.php @@ -0,0 +1,17 @@ +files = $container->get(FilesInterface::class); - $dirs = $container->get(DirectoriesInterface::class); - - $this->rootDir = $dirs->get('root'); - $this->vendorDir = $this->rootDir.'/vendor'; - - if ($this->files->exists($path = $this->vendorDir.'/composer/installed.json')) { - $installed = \json_decode($this->files->read($path), true); - $packages = $installed['packages'] ?? $installed; - - foreach ($packages as $package) { - if (! isset($package['extra']['spiral'])) { - continue; - } - - $packageName = $this->formatPackageName($package['name']); - $this->packages[$packageName] = \array_merge([ - 'bootloaders' => [], - 'dont-discover' => [], - ], (array)$package['extra']['spiral']); - } - } - } - - public function getBootloaders(): array - { - $bootloaders = []; - - foreach ($this->packages as $extra) { - $bootloaders = array_merge($bootloaders, (array)$extra['bootloaders']); - } - - return \array_unique($bootloaders); - } - - public function getIgnorableBootloaders(): array - { - if (! $this->files->isFile($composerPath = $this->rootDir.'/composer.json')) { - return []; - } - - $data = \json_decode( - file_get_contents($composerPath), - true - ); - - $ignore = (array)($data['extra']['spiral']['dont-discover'] ?? []); - - foreach ($this->packages as $extra) { - $ignore = array_merge($ignore, (array)$extra['dont-discover']); - } - - $bootloaders = []; - - foreach ($ignore as $packageName) { - if (isset($this->packages[$packageName])) { - $bootloaders = \array_merge($bootloaders, $this->packages[$packageName]['bootloaders']); - } - } - - return \array_unique($bootloaders); - } - - private function formatPackageName(string $name): string - { - return str_replace($this->vendorDir.'/', '', $name); - } -} diff --git a/src/Registry/ConfigRegistry.php b/src/Registry/ConfigRegistry.php deleted file mode 100644 index 9c5ac6f..0000000 --- a/src/Registry/ConfigRegistry.php +++ /dev/null @@ -1,29 +0,0 @@ -config = $container->get(BootloadersConfig::class); - } - - public function getBootloaders(): array - { - return $this->config->getBootloaders(); - } - - public function getIgnorableBootloaders(): array - { - return $this->config->getIgnorableBootloaders(); - } -} diff --git a/src/RegistryInterface.php b/src/RegistryInterface.php index 5fb9940..fa92ea3 100644 --- a/src/RegistryInterface.php +++ b/src/RegistryInterface.php @@ -2,21 +2,11 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover; +namespace Spiral\Discoverer; use Spiral\Core\Container; interface RegistryInterface { public function init(Container $container): void; - - /** - * @return array|array> - */ - public function getBootloaders(): array; - - /** - * @return array - */ - public function getIgnorableBootloaders(): array; } diff --git a/src/Tokenizer/ComposerRegistry.php b/src/Tokenizer/ComposerRegistry.php new file mode 100644 index 0000000..4fc88bf --- /dev/null +++ b/src/Tokenizer/ComposerRegistry.php @@ -0,0 +1,40 @@ +getComposer()->getPackages() as $package => $extra) { + $dirs = \array_merge( + $dirs, + \array_map(function (string $dir) use ($package, $extra) { + return $this->getComposer()->getPackagePath($package).'/'.ltrim($dir, '\/'); + }, $extra['directories'] ?? []) + ); + } + + foreach ($this->getComposer()->getComposerExtra('directories', []) as $package => $packageDirs) { + if (\is_int($package) || $package === 'self') { + $packagePath = rtrim($this->getComposer()->getRootDir(), '\/'); + } else if (! $packagePath = $this->getComposer()->getPackagePath($package)) { + continue; + } + + foreach ((array)$packageDirs as $dir) { + $dirs[] = $packagePath.'/'.ltrim($dir, '\/'); + } + } + + return $dirs; + } +} diff --git a/src/Tokenizer/DirectoriesDiscoverer.php b/src/Tokenizer/DirectoriesDiscoverer.php new file mode 100644 index 0000000..1314d7b --- /dev/null +++ b/src/Tokenizer/DirectoriesDiscoverer.php @@ -0,0 +1,44 @@ +registries = $registry; + } + + public static function getName(): string + { + return 'directories'; + } + + public function discover(): array + { + $dirs = []; + + foreach ($this->registries as $registry) { + $dirs = \array_merge($dirs, $registry->getDirectories()); + } + + return \array_values(\array_unique(\array_filter($dirs))); + } + + public function init(Container $container): void + { + foreach ($this->registries as $registry) { + $registry->init($container); + } + } +} diff --git a/src/Tokenizer/DirectoryRegistryInterface.php b/src/Tokenizer/DirectoryRegistryInterface.php new file mode 100644 index 0000000..a55a31f --- /dev/null +++ b/src/Tokenizer/DirectoryRegistryInterface.php @@ -0,0 +1,12 @@ +get(DirectoriesInterface::class); + + $this->composer = new Composer( + $container->get(FilesInterface::class), + $dirs->get('root') + ); + } + + public function getComposer(): Composer + { + return $this->composer; + } +} diff --git a/src/WithBootloadersDiscovering.php b/src/WithBootloadersDiscovering.php deleted file mode 100644 index 8cc1cb1..0000000 --- a/src/WithBootloadersDiscovering.php +++ /dev/null @@ -1,24 +0,0 @@ -discoverer = new Discoverer(...$registry); - } - - protected function defineBootloaders(): array - { - if (! $this->discoverer) { - return parent::defineBootloaders(); - } - - return $this->discoverer->discover($this->container); - } -} diff --git a/src/WithDiscovering.php b/src/WithDiscovering.php new file mode 100644 index 0000000..e3d59a9 --- /dev/null +++ b/src/WithDiscovering.php @@ -0,0 +1,31 @@ +discoverer = new Discoverer($this->container, ...$registry); + + $this->container->bindSingleton( + DiscovererInterface::class, + $this->discoverer + ); + } + + protected function defineBootloaders(): array + { + if (! $this->discoverer) { + return parent::defineBootloaders(); + } + + return $this->discoverer->discover(BootloadersDiscoverer::getName()); + } +} diff --git a/tests/composer.json b/tests/composer.json index af5c8de..c22f9f0 100644 --- a/tests/composer.json +++ b/tests/composer.json @@ -7,7 +7,18 @@ ], "dont-discover": [ "spiral-packages/event-bus" - ] + ], + "directories": { + "self": [ + "src/Listener" + ], + "spiral-packages/notifications": [ + "src/foo", + "src/bar" + ], + "spiral-packages/event-bus": "src/bar", + "spiral-packages/test": "src/bar" + } } } } diff --git a/tests/src/Registry/ArrayRegistryTest.php b/tests/src/Bootloader/ArrayRegistryTest.php similarity index 69% rename from tests/src/Registry/ArrayRegistryTest.php rename to tests/src/Bootloader/ArrayRegistryTest.php index 22b7d54..f85a37f 100644 --- a/tests/src/Registry/ArrayRegistryTest.php +++ b/tests/src/Bootloader/ArrayRegistryTest.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover\Tests\Registry; +namespace Spiral\Discoverer\Tests\Bootloader; -use Spiral\BootloadersDiscover\Registry\ArrayRegistry; -use Spiral\BootloadersDiscover\Tests\TestCase; +use Spiral\Discoverer\Bootloader\ArrayRegistry; +use Spiral\Discoverer\Tests\TestCase; final class ArrayRegistryTest extends TestCase { @@ -26,6 +26,6 @@ public function testGetsIgnorableBootloaders(): void 'BootloaderB', ]); - $this->assertSame($bootloaders, $registry->getIgnorableBootloaders()); + $this->assertSame($bootloaders, $registry->getIgnoredBootloaders()); } } diff --git a/tests/src/Bootloader/BootloadersDiscovererTest.php b/tests/src/Bootloader/BootloadersDiscovererTest.php new file mode 100644 index 0000000..a990511 --- /dev/null +++ b/tests/src/Bootloader/BootloadersDiscovererTest.php @@ -0,0 +1,56 @@ +assertSame( + 'bootloaders', + BootloadersDiscoverer::getName() + ); + } + + public function testDiscover(): void + { + $discover = new BootloadersDiscoverer( + $registry2 = $this->mockContainer(BootloaderRegistryInterface::class), + $registry3 = $this->mockContainer(BootloaderRegistryInterface::class), + ); + + $registry2->shouldReceive('init')->once()->with($this->getContainer()); + $registry3->shouldReceive('init')->once()->with($this->getContainer()); + + $registry3->shouldReceive('getIgnoredBootloaders')->once()->andReturn([ + 'BootloaderC', + ]); + + $registry2->shouldReceive('getIgnoredBootloaders')->once()->andReturn([ + 'BootloaderA', + ]); + + $registry3->shouldReceive('getBootloaders')->once()->andReturn([ + 'BootloaderA', + 'BootloaderB', + ]); + + $registry2->shouldReceive('getBootloaders')->once()->andReturn([ + 'BootloaderC', + 'BootloaderD', + ]); + + $discover->init($this->getContainer()); + + $this->assertSame([ + 'BootloaderD' => [], + 'BootloaderB' => [], + ], $discover->discover()); + } +} diff --git a/tests/src/Registry/ComposerRegistryTest.php b/tests/src/Bootloader/ComposerRegistryTest.php similarity index 82% rename from tests/src/Registry/ComposerRegistryTest.php rename to tests/src/Bootloader/ComposerRegistryTest.php index 736d68e..1373de7 100644 --- a/tests/src/Registry/ComposerRegistryTest.php +++ b/tests/src/Bootloader/ComposerRegistryTest.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover\Tests\Registry; +namespace Spiral\BootloadersDiscover\Tests\Bootloader; -use Spiral\BootloadersDiscover\Registry\ComposerRegistry; -use Spiral\BootloadersDiscover\Tests\TestCase; +use Spiral\Discoverer\Bootloader\ComposerRegistry; +use Spiral\Discoverer\Tests\TestCase; final class ComposerRegistryTest extends TestCase { @@ -35,6 +35,6 @@ public function testGetIgnorableBootloaders(): void $this->assertSame([ 'Spiral\EventBus\Bootloader\EventBusBootloader', 'Spiral\PackageB\Bootloader\PackageBBootloader', - ], $this->registry->getIgnorableBootloaders()); + ], $this->registry->getIgnoredBootloaders()); } } diff --git a/tests/src/Registry/ConfigRegistryTest.php b/tests/src/Bootloader/ConfigRegistryTest.php similarity index 58% rename from tests/src/Registry/ConfigRegistryTest.php rename to tests/src/Bootloader/ConfigRegistryTest.php index cefd6c2..3644873 100644 --- a/tests/src/Registry/ConfigRegistryTest.php +++ b/tests/src/Bootloader/ConfigRegistryTest.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover\Tests\Registry; +namespace Spiral\Discoverer\Tests\Bootloader; -use Spiral\BootloadersDiscover\Config\BootloadersConfig; -use Spiral\BootloadersDiscover\Registry\ConfigRegistry; -use Spiral\BootloadersDiscover\Tests\TestCase; +use Spiral\Discoverer\Config\DiscovererConfig; +use Spiral\Discoverer\Bootloader\ConfigRegistry; +use Spiral\Discoverer\Tests\TestCase; final class ConfigRegistryTest extends TestCase { @@ -16,12 +16,12 @@ protected function setUp(): void { parent::setUp(); - $this->getContainer()->bindSingleton(BootloadersConfig::class, new BootloadersConfig([ + $this->getContainer()->bindSingleton(DiscovererConfig::class, new DiscovererConfig([ 'bootloaders' => [ 'BootloaderA', 'BootloaderB', ], - 'ignorableBootloaders' => [ + 'ignoredBootloaders' => [ 'BootloaderC', 'BootloaderC', ], @@ -33,21 +33,17 @@ protected function setUp(): void public function testGetsBootloaders(): void { - $registry = new ConfigRegistry(); - $this->assertSame([ 'BootloaderA', 'BootloaderB', ], $this->registry->getBootloaders()); } - public function testGetsIgnorableBootloaders(): void + public function testGetsIgnoredBootloaders(): void { - $registry = new ConfigRegistry(); - $this->assertSame([ 'BootloaderC', 'BootloaderC', - ], $this->registry->getIgnorableBootloaders()); + ], $this->registry->getIgnoredBootloaders()); } } diff --git a/tests/src/DiscovererBootloaderWithDiscovererTest.php b/tests/src/DiscovererBootloaderWithDiscovererTest.php new file mode 100644 index 0000000..262b04a --- /dev/null +++ b/tests/src/DiscovererBootloaderWithDiscovererTest.php @@ -0,0 +1,39 @@ +discoverer = m::mock(DiscovererInterface::class); + $this->beforeBooting(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 d202641..86d0758 100644 --- a/tests/src/DiscovererTest.php +++ b/tests/src/DiscovererTest.php @@ -2,44 +2,53 @@ declare(strict_types=1); -namespace Spiral\BootloadersDiscover\Tests; +namespace Spiral\Discoverer\Tests; -use Spiral\BootloadersDiscover\Discoverer; -use Spiral\BootloadersDiscover\RegistryInterface; +use Spiral\Core\Container; +use Spiral\Discoverer\Discoverer; +use Spiral\Discoverer\DiscovererRegistryInterface; +use Spiral\Discoverer\Exception\DiscovererRegistryException; final class DiscovererTest extends TestCase { - public function testDiscover(): void + public function testDiscoverExistsDiscoverer(): void { - $discover = new Discoverer( - $registry1 = $this->mockContainer(RegistryInterface::class), - $registry2 = $this->mockContainer(RegistryInterface::class), + $discoverer = new Discoverer( + $this->getContainer(), + new class implements DiscovererRegistryInterface { + + public static function getName(): string + { + return 'test'; + } + + public function discover(): array + { + return [ + 'foo', + 'bar', + ]; + } + + public function init(Container $container): void + { + + } + } ); - $registry1->shouldReceive('init')->once()->with($this->getContainer()); - $registry2->shouldReceive('init')->once()->with($this->getContainer()); - - $registry1->shouldReceive('getIgnorableBootloaders')->once()->andReturn([ - 'BootloaderC', - ]); - - $registry2->shouldReceive('getIgnorableBootloaders')->once()->andReturn([ - 'BootloaderA', - ]); - - $registry1->shouldReceive('getBootloaders')->once()->andReturn([ - 'BootloaderA', - 'BootloaderB', - ]); + $this->assertSame([ + 'foo', + 'bar' + ], $discoverer->discover('test')); + } - $registry2->shouldReceive('getBootloaders')->once()->andReturn([ - 'BootloaderC', - 'BootloaderD', - ]); + public function testNonExistDiscovererShouldThrowAnException() + { + $this->expectException(DiscovererRegistryException::class); + $this->expectErrorMessage('Registry with name [test] does not exist.'); - $this->assertSame([ - 'BootloaderB' => [], - 'BootloaderD' => [], - ], $discover->discover($this->getContainer())); + $discoverer = new Discoverer($this->getContainer()); + $discoverer->discover('test'); } } diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php index f5bd307..f969f44 100644 --- a/tests/src/TestCase.php +++ b/tests/src/TestCase.php @@ -1,6 +1,8 @@ registry = new ComposerRegistry(); + $this->registry->init($this->getContainer()); + } + + public function testGetsBootloaders(): void + { + $rootDir = $this->getDirectoryByAlias('root'); + + $this->assertSame([ + $rootDir.'vendor/composer/../foo/scheduler/src/foo', + $rootDir.'src/Listener', + $rootDir.'vendor/composer/../foo/notifications/src/foo', + $rootDir.'vendor/composer/../foo/notifications/src/bar', + $rootDir.'vendor/composer/../foo/event-bus/src/bar', + ], $this->registry->getDirectories()); + } +} diff --git a/tests/src/Tokenizer/DirectoriesDiscovererTest.php b/tests/src/Tokenizer/DirectoriesDiscovererTest.php new file mode 100644 index 0000000..b19003a --- /dev/null +++ b/tests/src/Tokenizer/DirectoriesDiscovererTest.php @@ -0,0 +1,51 @@ +assertSame( + 'directories', + DirectoriesDiscoverer::getName() + ); + } + + public function testDiscover(): void + { + $discover = new DirectoriesDiscoverer( + $registry2 = $this->mockContainer(DirectoryRegistryInterface::class), + $registry3 = $this->mockContainer(DirectoryRegistryInterface::class), + ); + + $registry2->shouldReceive('init')->once()->with($this->getContainer()); + $registry3->shouldReceive('init')->once()->with($this->getContainer()); + + $discover->init($this->getContainer()); + + $registry3->shouldReceive('getDirectories')->once()->andReturn([ + 'src/foo', + 'src/bar', + ]); + + $registry2->shouldReceive('getDirectories')->once()->andReturn([ + 'src/foo', + 'src/baz', + null, + '', + ]); + + $this->assertSame([ + 'src/foo', + 'src/baz', + 'src/bar', + ], $discover->discover()); + } +}