-
-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add debug command #372
base: master
Are you sure you want to change the base?
Add debug command #372
Changes from all commits
624482f
bd2dcea
e06790e
19dc9cf
8e0d0fe
f1d6923
da38a9f
79b8ef6
c5eae16
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Yiisoft\Di\Command\DebugContainerCommand; | ||
|
||
return [ | ||
'yiisoft/yii-debug' => [ | ||
'ignoredCommands' => [ | ||
'debug:container', | ||
], | ||
], | ||
'yiisoft/yii-console' => [ | ||
'commands' => [ | ||
'debug:container' => DebugContainerCommand::class, | ||
], | ||
], | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Yiisoft\Di\Command; | ||
|
||
use Psr\Container\ContainerInterface; | ||
use ReflectionClass; | ||
use Symfony\Component\Console\Attribute\AsCommand; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Helper\Table; | ||
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Input\InputOption; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\Console\Style\SymfonyStyle; | ||
use Yiisoft\Config\ConfigInterface; | ||
use Yiisoft\Definitions\ArrayDefinition; | ||
use Yiisoft\Definitions\CallableDefinition; | ||
use Yiisoft\Definitions\ValueDefinition; | ||
use Yiisoft\Di\Helpers\DefinitionNormalizer; | ||
use Yiisoft\VarDumper\VarDumper; | ||
|
||
#[AsCommand( | ||
name: 'debug:container', | ||
description: 'Show information about container', | ||
)] | ||
final class DebugContainerCommand extends Command | ||
{ | ||
public function __construct( | ||
private readonly ContainerInterface $container, | ||
) { | ||
parent::__construct(); | ||
} | ||
|
||
protected function configure(): void | ||
{ | ||
$this | ||
->addArgument('id', InputArgument::IS_ARRAY, 'Service ID') | ||
->addOption('groups', null, InputOption::VALUE_NONE, 'Show groups') | ||
->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'Show group'); | ||
} | ||
|
||
protected function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
$config = $this->container->get(ConfigInterface::class); | ||
|
||
$io = new SymfonyStyle($input, $output); | ||
|
||
if ($input->hasArgument('id') && !empty($ids = $input->getArgument('id'))) { | ||
$build = $this->getConfigBuild($config); | ||
foreach ($ids as $id) { | ||
$definition = null; | ||
foreach ($build as $definitions) { | ||
if (array_key_exists($id, $definitions)) { | ||
Check failure on line 55 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 55 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 55 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
Check failure on line 55 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
$definition = $definitions[$id]; | ||
} | ||
} | ||
if ($definition === null) { | ||
$io->error( | ||
sprintf( | ||
'Service "%s" not found.', | ||
$id, | ||
Check failure on line 63 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 63 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
) | ||
); | ||
continue; | ||
} | ||
$io->title($id); | ||
Check failure on line 68 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 68 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
|
||
$normalizedDefinition = DefinitionNormalizer::normalize($definition, $id); | ||
if ($normalizedDefinition instanceof ArrayDefinition) { | ||
$definitionList = ['ID' => $id]; | ||
if (class_exists($normalizedDefinition->getClass())) { | ||
$definitionList[] = ['Class' => $normalizedDefinition->getClass()]; | ||
} | ||
if (!empty($normalizedDefinition->getConstructorArguments())) { | ||
$definitionList[] = [ | ||
'Constructor' => $this->export( | ||
$normalizedDefinition->getConstructorArguments() | ||
), | ||
]; | ||
} | ||
if (!empty($normalizedDefinition->getMethodsAndProperties())) { | ||
$definitionList[] = [ | ||
'Methods' => $this->export( | ||
$normalizedDefinition->getMethodsAndProperties() | ||
), | ||
]; | ||
} | ||
if (isset($definition['tags'])) { | ||
$definitionList[] = ['Tags' => $this->export($definition['tags'])]; | ||
Check failure on line 91 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArrayAccess
Check failure on line 91 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArrayAccess
|
||
} | ||
|
||
$io->definitionList(...$definitionList); | ||
|
||
continue; | ||
} | ||
if ($normalizedDefinition instanceof CallableDefinition || $normalizedDefinition instanceof ValueDefinition) { | ||
$io->text( | ||
$this->export($definition) | ||
); | ||
continue; | ||
} | ||
|
||
$output->writeln([ | ||
$id, | ||
VarDumper::create($normalizedDefinition)->asString(), | ||
]); | ||
} | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
if ($input->hasOption('groups') && $input->getOption('groups')) { | ||
$build = $this->getConfigBuild($config); | ||
$groups = array_keys($build); | ||
sort($groups); | ||
|
||
$io->table(['Groups'], array_map(static fn ($group) => [$group], $groups)); | ||
|
||
return self::SUCCESS; | ||
} | ||
if ($input->hasOption('group') && !empty($group = $input->getOption('group'))) { | ||
$data = $config->get($group); | ||
Check failure on line 124 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 124 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
ksort($data); | ||
|
||
$rows = $this->getGroupServices($data); | ||
|
||
$table = new Table($output); | ||
$table | ||
->setHeaderTitle($group) | ||
->setHeaders(['Service', 'Definition']) | ||
->setRows($rows); | ||
$table->render(); | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
$build = $this->getConfigBuild($config); | ||
|
||
foreach ($build as $group => $data) { | ||
$rows = $this->getGroupServices($data); | ||
Check failure on line 142 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 142 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
|
||
$table = new Table($output); | ||
$table | ||
->setHeaderTitle($group) | ||
Check failure on line 146 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgumentTypeCoercion
Check failure on line 146 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgumentTypeCoercion
|
||
->setHeaders(['Group', 'Services']) | ||
->setRows($rows); | ||
$table->render(); | ||
} | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
private function getConfigBuild(mixed $config): array | ||
Check failure on line 155 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedInferredReturnType
Check failure on line 155 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedInferredReturnType
|
||
{ | ||
$reflection = new ReflectionClass($config); | ||
Check failure on line 157 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.3-ubuntu-latestMixedArgument
Check failure on line 157 in src/Command/DebugContainerCommand.php GitHub Actions / psalm / PHP 8.1-ubuntu-latestMixedArgument
|
||
$buildReflection = $reflection->getProperty('build'); | ||
$buildReflection->setAccessible(true); | ||
return $buildReflection->getValue($config); | ||
} | ||
|
||
protected function getGroupServices(array $data): array | ||
{ | ||
$rows = []; | ||
foreach ($data as $id => $definition) { | ||
$class = ''; | ||
if (is_string($definition)) { | ||
$class = $definition; | ||
} | ||
if (is_array($definition)) { | ||
$class = $definition['class'] ?? $id; | ||
} | ||
if (is_object($definition)) { | ||
$class = $definition::class; | ||
} | ||
|
||
$rows[] = [ | ||
$id, | ||
$class, | ||
]; | ||
} | ||
return $rows; | ||
} | ||
|
||
protected function export(mixed $value): string | ||
{ | ||
return VarDumper::create($value)->asString(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Yiisoft\Di\Tests\Unit\Command; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Psr\Container\ContainerInterface; | ||
use Psr\Log\LoggerInterface; | ||
use Psr\Log\NullLogger; | ||
use Symfony\Component\Console\Tester\CommandTester; | ||
use Yiisoft\Config\Config; | ||
use Yiisoft\Config\ConfigInterface; | ||
use Yiisoft\Config\ConfigPaths; | ||
use Yiisoft\Di\Command\DebugContainerCommand; | ||
use Yiisoft\Di\Container; | ||
use Yiisoft\Di\ContainerConfig; | ||
|
||
final class DebugContainerCommandTest extends TestCase | ||
{ | ||
public function testCommand(): void | ||
{ | ||
$container = $this->createContainer(); | ||
$config = $container->get(ConfigInterface::class); | ||
// trigger config build | ||
$config->get('params'); | ||
|
||
$command = new DebugContainerCommand($container); | ||
$commandTester = new CommandTester($command); | ||
|
||
$commandTester->execute([]); | ||
|
||
$this->assertEquals(0, $commandTester->getStatusCode()); | ||
} | ||
|
||
private function createContainer(): ContainerInterface | ||
{ | ||
$config = ContainerConfig::create() | ||
->withDefinitions([ | ||
LoggerInterface::class => NullLogger::class, | ||
ConfigInterface::class => [ | ||
'class' => Config::class, | ||
'__construct()' => [ | ||
new ConfigPaths(__DIR__ . '/config'), | ||
], | ||
], | ||
]); | ||
return new Container($config); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
return [ | ||
'/'=>[ | ||
'params' => [ | ||
|
||
] | ||
], | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
return [ | ||
'/' => [ | ||
'params' => [ | ||
'yiitest/yii-debug' => [ | ||
'param1.php', | ||
], | ||
], | ||
], | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to debug tags. Or can I pass
tag@tag-name
as an id?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you please elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to see all services with given tag. Is it possible now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yiisoft/config must provide API to explore the config files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BoShurik no, it is not possible yet.