diff --git a/CHANGELOG.md b/CHANGELOG.md
index fabcd3c5b..dfc23ce9c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
- [spiral/monolog-bridge] Added the ability to configure the **Monolog** messages format via environment variable
`MONOLOG_FORMAT`.
- [spiral/translator] Added the ability to register additional locales directories.
+ - [spiral/prototype] Added console command `Spiral\Prototype\Command\ListCommand` for listing prototype dependencies.
## 3.8.4 - 2023-09-08
diff --git a/src/Prototype/src/Bootloader/PrototypeBootloader.php b/src/Prototype/src/Bootloader/PrototypeBootloader.php
index ea1ddfebf..ddba47686 100644
--- a/src/Prototype/src/Bootloader/PrototypeBootloader.php
+++ b/src/Prototype/src/Bootloader/PrototypeBootloader.php
@@ -80,6 +80,7 @@ public function init(ConsoleBootloader $console): void
{
$console->addCommand(Command\DumpCommand::class);
$console->addCommand(Command\ListCommand::class);
+ $console->addCommand(Command\UsageCommand::class);
$console->addCommand(Command\InjectCommand::class);
$console->addConfigureSequence(
diff --git a/src/Prototype/src/Command/ListCommand.php b/src/Prototype/src/Command/ListCommand.php
index ea7ee333b..bcae5f706 100644
--- a/src/Prototype/src/Command/ListCommand.php
+++ b/src/Prototype/src/Command/ListCommand.php
@@ -6,27 +6,22 @@
final class ListCommand extends AbstractCommand
{
- public const NAME = 'prototype:list';
- public const DESCRIPTION = 'List all prototyped classes';
+ public const NAME = 'prototype:list';
+ public const DESCRIPTION = 'List all declared prototype dependencies';
- /**
- * List all prototype classes.
- */
public function perform(): int
{
- $prototyped = $this->locator->getTargetClasses();
- if ($prototyped === []) {
- $this->writeln('No prototyped classes found.');
+ $bindings = $this->registry->getPropertyBindings();
+ if ($bindings === []) {
+ $this->comment('No prototype dependencies found.');
return self::SUCCESS;
}
- $grid = $this->table(['Class:', 'Property:', 'Target:']);
+ $grid = $this->table(['Name:', 'Target:']);
- foreach ($prototyped as $class) {
- $proto = $this->getPrototypeProperties($class, $prototyped);
-
- $grid->addRow([$class->getName(), $this->mergeNames($proto), $this->mergeTargets($proto)]);
+ foreach ($bindings as $binding) {
+ $grid->addRow([$binding->property, $binding->type->name()]);
}
$grid->render();
diff --git a/src/Prototype/src/Command/UsageCommand.php b/src/Prototype/src/Command/UsageCommand.php
new file mode 100644
index 000000000..66f64057e
--- /dev/null
+++ b/src/Prototype/src/Command/UsageCommand.php
@@ -0,0 +1,36 @@
+locator->getTargetClasses();
+ if ($prototyped === []) {
+ $this->writeln('No prototyped classes found.');
+
+ return self::SUCCESS;
+ }
+
+ $grid = $this->table(['Class:', 'Property:', 'Target:']);
+
+ foreach ($prototyped as $class) {
+ $proto = $this->getPrototypeProperties($class, $prototyped);
+
+ $grid->addRow([$class->getName(), $this->mergeNames($proto), $this->mergeTargets($proto)]);
+ }
+
+ $grid->render();
+
+ return self::SUCCESS;
+ }
+}
diff --git a/src/Prototype/tests/Commands/InjectCommandTest.php b/src/Prototype/tests/Commands/InjectCommandTest.php
index fcfbefdb9..1577908a9 100644
--- a/src/Prototype/tests/Commands/InjectCommandTest.php
+++ b/src/Prototype/tests/Commands/InjectCommandTest.php
@@ -16,6 +16,16 @@
class InjectCommandTest extends AbstractCommandsTestCase
{
+ public function testCommandRegistered(): void
+ {
+ $out = new BufferedOutput();
+ $this->app->get(Console::class)->run('list', new ArrayInput([]), $out);
+
+ $result = $out->fetch();
+
+ $this->assertStringContainsString('prototype:inject', $result);
+ }
+
public function testEmptyInjection(): void
{
$target = EmptyInjectionClass::class;
diff --git a/src/Prototype/tests/Commands/ListCommandTest.php b/src/Prototype/tests/Commands/ListCommandTest.php
index 5a7f674a8..d12bb6956 100644
--- a/src/Prototype/tests/Commands/ListCommandTest.php
+++ b/src/Prototype/tests/Commands/ListCommandTest.php
@@ -9,60 +9,90 @@
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
-class ListCommandTest extends AbstractCommandsTestCase
+final class ListCommandTest extends AbstractCommandsTestCase
{
- public function testList(): void
+ public function testCommandRegistered(): void
{
- $inp = new ArrayInput([]);
$out = new BufferedOutput();
- $this->app->get(Console::class)->run('list', $inp, $out);
+ $this->app->get(Console::class)->run('list', new ArrayInput([]), $out);
$result = $out->fetch();
$this->assertStringContainsString('prototype:list', $result);
- $this->assertStringContainsString('prototype:inject', $result);
- }
-
- public function testPrototypes(): void
- {
- $inp = new ArrayInput([]);
- $out = new BufferedOutput();
- $this->app->get(Console::class)->run('prototype:list', $inp, $out);
-
- $result = $out->fetch();
-
- $this->assertStringContainsString('testClass', $result);
- $this->assertStringContainsString('undefined', $result);
}
- public function testPrototypesBound(): void
- {
- $this->app->bindApp();
-
- $inp = new ArrayInput([]);
- $out = new BufferedOutput();
- $this->app->get(Console::class)->run('prototype:list', $inp, $out);
-
- $result = $out->fetch();
-
- $this->assertStringContainsString('testClass', $result);
- $this->assertStringNotContainsString('undefined', $result);
- $this->assertStringNotContainsString('Undefined class', $result);
- $this->assertStringContainsString(TestApp::class, $result);
- }
-
- public function testPrototypesBoundWithoutResolve(): void
+ public function testList(): void
{
- $this->app->bindWithoutResolver();
-
- $inp = new ArrayInput([]);
$out = new BufferedOutput();
- $this->app->get(Console::class)->run('prototype:list', $inp, $out);
+ $this->app->get(Console::class)->run('prototype:list', new ArrayInput([]), $out);
$result = $out->fetch();
- $this->assertStringContainsString('testClass', $result);
- $this->assertStringContainsString('Can\'t resolve', $result);
+ $this->assertStringContainsString('app', $result);
$this->assertStringContainsString(TestApp::class, $result);
+ $this->assertStringContainsString('classLocator', $result);
+ $this->assertStringContainsString(\Spiral\Tokenizer\ClassesInterface::class, $result);
+ $this->assertStringContainsString('console', $result);
+ $this->assertStringContainsString(\Spiral\Console\Console::class, $result);
+ $this->assertStringContainsString('broadcast', $result);
+ $this->assertStringContainsString(\Spiral\Broadcasting\BroadcastInterface::class, $result);
+ $this->assertStringContainsString('container', $result);
+ $this->assertStringContainsString(\Psr\Container\ContainerInterface::class, $result);
+ $this->assertStringContainsString('encrypter', $result);
+ $this->assertStringContainsString(\Spiral\Encrypter\EncrypterInterface::class, $result);
+ $this->assertStringContainsString('env', $result);
+ $this->assertStringContainsString(\Spiral\Boot\EnvironmentInterface::class, $result);
+ $this->assertStringContainsString('files', $result);
+ $this->assertStringContainsString(\Spiral\Files\FilesInterface::class, $result);
+ $this->assertStringContainsString('guard', $result);
+ $this->assertStringContainsString(\Spiral\Security\GuardInterface::class, $result);
+ $this->assertStringContainsString('http', $result);
+ $this->assertStringContainsString(\Spiral\Http\Http::class, $result);
+ $this->assertStringContainsString('i18n', $result);
+ $this->assertStringContainsString(\Spiral\Translator\TranslatorInterface::class, $result);
+ $this->assertStringContainsString('input', $result);
+ $this->assertStringContainsString(\Spiral\Http\Request\InputManager::class, $result);
+ $this->assertStringContainsString('session', $result);
+ $this->assertStringContainsString(\Spiral\Session\SessionScope::class, $result);
+ $this->assertStringContainsString('cookies', $result);
+ $this->assertStringContainsString(\Spiral\Cookies\CookieManager::class, $result);
+ $this->assertStringContainsString('logger', $result);
+ $this->assertStringContainsString(\Psr\Log\LoggerInterface::class, $result);
+ $this->assertStringContainsString('logs', $result);
+ $this->assertStringContainsString(\Spiral\Logger\LogsInterface::class, $result);
+ $this->assertStringContainsString('memory', $result);
+ $this->assertStringContainsString(\Spiral\Boot\MemoryInterface::class, $result);
+ $this->assertStringContainsString('paginators', $result);
+ $this->assertStringContainsString(\Spiral\Pagination\PaginationProviderInterface::class, $result);
+ $this->assertStringContainsString('queue', $result);
+ $this->assertStringContainsString(\Spiral\Queue\QueueInterface::class, $result);
+ $this->assertStringContainsString('queueManager', $result);
+ $this->assertStringContainsString(\Spiral\Queue\QueueConnectionProviderInterface::class, $result);
+ $this->assertStringContainsString('request', $result);
+ $this->assertStringContainsString(\Spiral\Http\Request\InputManager::class, $result);
+ $this->assertStringContainsString('response', $result);
+ $this->assertStringContainsString(\Spiral\Http\ResponseWrapper::class, $result);
+ $this->assertStringContainsString('router', $result);
+ $this->assertStringContainsString(\Spiral\Router\RouterInterface::class, $result);
+ $this->assertStringContainsString('snapshots', $result);
+ $this->assertStringContainsString(\Spiral\Snapshots\SnapshotterInterface::class, $result);
+ $this->assertStringContainsString('storage', $result);
+ $this->assertStringContainsString(\Spiral\Storage\BucketInterface::class, $result);
+ $this->assertStringContainsString('serializer', $result);
+ $this->assertStringContainsString(\Spiral\Serializer\SerializerManager::class, $result);
+ $this->assertStringContainsString('validator', $result);
+ $this->assertStringContainsString(\Spiral\Validation\ValidationInterface::class, $result);
+ $this->assertStringContainsString('views', $result);
+ $this->assertStringContainsString(\Spiral\Views\ViewsInterface::class, $result);
+ $this->assertStringContainsString('auth', $result);
+ $this->assertStringContainsString(\Spiral\Auth\AuthScope::class, $result);
+ $this->assertStringContainsString('authTokens', $result);
+ $this->assertStringContainsString(\Spiral\Auth\TokenStorageInterface::class, $result);
+ $this->assertStringContainsString('cache', $result);
+ $this->assertStringContainsString(\Psr\SimpleCache\CacheInterface::class, $result);
+ $this->assertStringContainsString('cacheManager', $result);
+ $this->assertStringContainsString(\Spiral\Cache\CacheStorageProviderInterface::class, $result);
+ $this->assertStringContainsString('exceptionHandler', $result);
+ $this->assertStringContainsString(\Spiral\Exceptions\ExceptionHandlerInterface::class, $result);
}
}
diff --git a/src/Prototype/tests/Commands/UsageCommandTest.php b/src/Prototype/tests/Commands/UsageCommandTest.php
new file mode 100644
index 000000000..cca71537e
--- /dev/null
+++ b/src/Prototype/tests/Commands/UsageCommandTest.php
@@ -0,0 +1,66 @@
+app->get(Console::class)->run('list', new ArrayInput([]), $out);
+
+ $result = $out->fetch();
+
+ $this->assertStringContainsString('prototype:usage', $result);
+ }
+
+ public function testPrototypes(): void
+ {
+ $inp = new ArrayInput([]);
+ $out = new BufferedOutput();
+ $this->app->get(Console::class)->run('prototype:usage', $inp, $out);
+
+ $result = $out->fetch();
+
+ $this->assertStringContainsString('testClass', $result);
+ $this->assertStringContainsString('undefined', $result);
+ }
+
+ public function testPrototypesBound(): void
+ {
+ $this->app->bindApp();
+
+ $inp = new ArrayInput([]);
+ $out = new BufferedOutput();
+ $this->app->get(Console::class)->run('prototype:usage', $inp, $out);
+
+ $result = $out->fetch();
+
+ $this->assertStringContainsString('testClass', $result);
+ $this->assertStringNotContainsString('undefined', $result);
+ $this->assertStringNotContainsString('Undefined class', $result);
+ $this->assertStringContainsString(TestApp::class, $result);
+ }
+
+ public function testPrototypesBoundWithoutResolve(): void
+ {
+ $this->app->bindWithoutResolver();
+
+ $inp = new ArrayInput([]);
+ $out = new BufferedOutput();
+ $this->app->get(Console::class)->run('prototype:usage', $inp, $out);
+
+ $result = $out->fetch();
+
+ $this->assertStringContainsString('testClass', $result);
+ $this->assertStringContainsString('Can\'t resolve', $result);
+ $this->assertStringContainsString(TestApp::class, $result);
+ }
+}