Skip to content

Add debug:live-component command#3364

Open
mbuliard wants to merge 2 commits intosymfony:2.xfrom
mbuliard:live-component-debug-command
Open

Add debug:live-component command#3364
mbuliard wants to merge 2 commits intosymfony:2.xfrom
mbuliard:live-component-debug-command

Conversation

@mbuliard
Copy link
Contributor

@mbuliard mbuliard commented Mar 5, 2026

Q A
Bug fix? no
New feature? yes
Deprecations? no
Documentation? todo
Issues Fix #...
License MIT

Hello,

In several applications, I am using multiple events to communicate between LiveComponents.
It sometimes take me a little time to find out mistakes concerning these events.

That's why I propose this small command for debug specific to LiveComponents.

This command is largely copied from @StevenRenaux TwigComponentDebugCommand.
As the previous one, it can be used for listing or to see details of one component :

  1. Listing : list all registered LiveComponents. Can filter those listening to an event with --listening filter.
image image
  1. Details : providing a LiveComponent name, display its LiveProps and LiveListeners.
image

@mbuliard mbuliard marked this pull request as ready for review March 5, 2026 17:15
@carsonbot carsonbot added Feature New Feature Status: Needs Work Additional work is needed labels Mar 5, 2026
@mbuliard mbuliard force-pushed the live-component-debug-command branch 2 times, most recently from a6096e3 to fec150f Compare March 5, 2026 19:29
@mbuliard mbuliard changed the title [WIP] LiveComponentDebugCommand LiveComponentDebugCommand Mar 5, 2026
@mbuliard mbuliard force-pushed the live-component-debug-command branch from fec150f to 8911a18 Compare March 6, 2026 16:58
@carsonbot carsonbot added Status: Needs Review Needs to be reviewed and removed Status: Needs Work Additional work is needed labels Mar 6, 2026
@carsonbot carsonbot changed the title LiveComponentDebugCommand LiveComponentDebugCommand Mar 6, 2026
@Kocal Kocal changed the title LiveComponentDebugCommand [LiveComponent] Add debug:live-component command Mar 7, 2026
Copy link
Member

@Kocal Kocal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, here some comments


To get specific information about a component, specify its name (or a part of it):

<info>php %command.full_name% Alert</info>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, let's reuse ProductSearch component from the official document, I don't see how a Alert can be a live component:

Suggested change
<info>php %command.full_name% Alert</info>
<info>php %command.full_name% ProductSearch </info>

if (\is_string($name)) {
$componentName = $this->findComponentName($io, $name, $input->isInteractive());
if (null === $componentName) {
$io->error(\sprintf('Unknown LiveComponent "%s".', $name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$io->error(\sprintf('Unknown LiveComponent "%s".', $name));
$io->error(\sprintf('Unknown component "%s".', $name));

return null;
}

private function listComponents(?string $eventFilter = null): array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private function listComponents(?string $eventFilter = null): array
/**
* @return array<string, LiveComponentMetadata>
*/
private function findComponents(?string $eventFilter = null): array

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we re-use the LiveComponentMetadataFactory & LiveComponentMetadata instead?

Comment on lines +110 to +112
if (null === $eventFilter) {
return $this->componentClassMap;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (null === $eventFilter) {
return $this->componentClassMap;
}
$components = [];
if (null === $eventFilter) {
foreach ($this->componentClassMap as $class => $name) {
$components[$name] ??= $this->liveComponentMetadataFactory->getMetadata($name);
}
return $components;
}

foreach ($this->componentClassMap as $name => $component) {
foreach (AsLiveComponent::liveListeners($component['class']) as $listener) {
if ($listener['event'] === $eventFilter) {
$filteredComponents[$name] = $component;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$filteredComponents[$name] = $component;
$components[$name] ??= $this->liveComponentMetadataFactory->getMetadata($name);

return $this->componentClassMap;
}

$filteredComponents = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$filteredComponents = [];

}
}

return $filteredComponents;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return $filteredComponents;
return $components;


private function displayComponentDetails(SymfonyStyle $io, string $name): void
{
$component = $this->componentClassMap[$name];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not directly depend on $componentClassMap but use the (internal) dedicated classes instead, that would makes things safer instead of relying on array-based and undocumented structures

Comment on lines +167 to +195
$properties = [];
$reflectionClass = new \ReflectionClass($class);
foreach ($reflectionClass->getProperties() as $property) {
if (!$property->isPublic()) {
continue;
}
if (empty($property->getAttributes(LiveProp::class))) {
continue;
}

$type = $this->displayType($property->getType());
$propertyName = '$'.$property->getName();
$defaultValueDisplay = $property->hasDefaultValue() ?
$this->displayDefaultValue($property->getDefaultValue()) :
'';
$arguments = $property->getAttributes(LiveProp::class)[0]->getArguments();
$argumentsDisplay = empty($arguments) ?
'' :
' ('.implode(', ', array_map(
static fn ($key, $value) => $key.': '.json_encode($value),
array_keys($arguments),
$arguments
)).')';

$propertyDisplay = $type.$propertyName.$defaultValueDisplay.$argumentsDisplay;
$properties[$property->name] = $propertyDisplay;
}

return $properties;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code cold be super-simplified by relying on getAllLivePropsMetadata() from LiveComponentMetadata (as suggested earlier)

$componentReferences[$tag['key']] = new Reference($id);
$componentNames[] = $tag['key'];
$componentClassMap[$tag['class']] = $tag['key'];
$definition->addTag('twig.component', $tag);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

@carsonbot carsonbot added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Mar 7, 2026
@carsonbot carsonbot changed the title [LiveComponent] Add debug:live-component command Add debug:live-component command Mar 7, 2026
@carsonbot carsonbot added Status: Needs Review Needs to be reviewed and removed Status: Needs Work Additional work is needed labels Mar 10, 2026
@mbuliard mbuliard force-pushed the live-component-debug-command branch 4 times, most recently from 1e20046 to a79410c Compare March 10, 2026 08:49
@mbuliard mbuliard force-pushed the live-component-debug-command branch from a79410c to 4880892 Compare March 10, 2026 08:55
@mbuliard
Copy link
Contributor Author

@Kocal Thanks for the review !
I added the LiveMetadataFactory in the command as you suggested but I had to tweak the LiveComponentMetadata->getAllLivePropsMetadata to work without an actual component.

I also don't see a way to compute the name of a component in the LiveComponentPass without using directly TwigComponent configurations, that's why I added the $definition->addTag('twig.component', $tag); in TwigComponentPass, to set the tag with the name computed.

@mbuliard mbuliard requested a review from Kocal March 11, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New Feature Status: Needs Review Needs to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants