Skip to content

Commit

Permalink
Debug LiveComponent EventListener
Browse files Browse the repository at this point in the history
  • Loading branch information
mbuliard committed Apr 11, 2024
1 parent a13900f commit 5eda833
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 17 deletions.
42 changes: 37 additions & 5 deletions src/TwigComponent/src/Command/TwigComponentDebugCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
use Symfony\Component\Console\Helper\TableSeparator;
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 Symfony\Component\Finder\Finder;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
use Symfony\UX\TwigComponent\ComponentFactory;
Expand Down Expand Up @@ -49,6 +51,11 @@ protected function configure(): void
$this
->setDefinition([
new InputArgument('name', InputArgument::OPTIONAL, 'A component name or part of the component name'),
new InputOption(
name: 'listening',
mode: InputOption::VALUE_REQUIRED,
description: 'Filter components list to display only those listening to the given action'
),
])
->setHelp(
<<<'EOF'
Expand Down Expand Up @@ -83,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::SUCCESS;
}

$components = $this->findComponents();
$components = $this->findComponents($input->getOption('listening'));
$this->displayComponentsTable($io, $components);

return Command::SUCCESS;
Expand Down Expand Up @@ -128,14 +135,19 @@ private function findComponentName(SymfonyStyle $io, string $name, bool $interac
/**
* @return array<string, ComponentMetadata>
*/
private function findComponents(): array
private function findComponents(?string $listeningFilter): array
{
$components = [];
foreach ($this->componentClassMap as $class => $name) {
$components[$name] ??= $this->componentFactory->metadataFor($name);
if (null === $listeningFilter || \in_array($listeningFilter, $this->resolveEventsListening($class))) {
$components[$name] ??= $this->componentFactory->metadataFor($name);
}
}
foreach ($this->findAnonymousComponents() as $name => $template) {
$components[$name] ??= $this->componentFactory->metadataFor($name);

if (null === $listeningFilter) {
foreach ($this->findAnonymousComponents() as $name => $template) {
$components[$name] ??= $this->componentFactory->metadataFor($name);
}
}

return $components;
Expand Down Expand Up @@ -196,6 +208,14 @@ private function displayComponentDetails(SymfonyStyle $io, string $name): void
['Properties', implode("\n", $this->getComponentProperties($metadata))],
]);

$eventsListened = $this->resolveEventsListening($metadata->get('class'));
if ($eventsListened) {
$table->addRows([
new TableSeparator(),
['Listening to', implode("\n", $eventsListened)],
]);
}

$logMethod = function (\ReflectionMethod $m) {
$params = array_map(
fn (\ReflectionParameter $p) => '$'.$p->getName(),
Expand Down Expand Up @@ -314,4 +334,16 @@ private function getAnonymousComponentProperties(ComponentMetadata $metadata): a

return $properties;
}

private function resolveEventsListening(string $class): array
{
$events = [];
foreach ((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
foreach ($method->getAttributes(LiveListener::class) as $listenAttribute) {
$events[] = $listenAttribute->getArguments()[0];
}
}

return $events;
}
}
14 changes: 14 additions & 0 deletions src/TwigComponent/src/DataCollector/TwigComponentDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\TwigComponent\Event\PostRenderEvent;
use Symfony\UX\TwigComponent\Event\PreRenderEvent;
use Symfony\UX\TwigComponent\EventListener\TwigComponentLoggerListener;
Expand Down Expand Up @@ -115,6 +116,7 @@ private function collectDataFromLogger(): void
'template_path' => $this->resolveTemplatePath($metadata->getTemplate()), // defer ? lazy ?
'render_count' => 0,
'render_time' => 0,
'listening' => $this->resolveEventsListening($componentClass),
];

$renderId = spl_object_id($mountedComponent);
Expand Down Expand Up @@ -182,4 +184,16 @@ private function resolveTemplatePath(string $logicalName): ?string

return $source->getPath();
}

private function resolveEventsListening(string $class): array
{
$events = [];
foreach ((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
foreach ($method->getAttributes(LiveListener::class) as $listenAttribute) {
$events[] = $listenAttribute->getArguments()[0];
}
}

return $events;
}
}
27 changes: 15 additions & 12 deletions src/TwigComponent/templates/Collector/twig_component.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@
{% else %}
<span class=text-muted">{{ component.template }}</span>
{% endif %}
{% if component.listening_to is not empty %}
<pre class="sf-dump"><span class="text-muted">Listening to {{ component.listening_to|join(', ') }}</span></pre>
{% endif %}
</td>
<td class="cell-right">{{ component.render_count }}</td>
<td class="cell-right">
Expand Down Expand Up @@ -285,18 +288,18 @@
</tr>
</thead>
<tbody id="render-{{ loop.index }}--details">
<tr class="{{ not render.input_props|default ? 'opacity-50' }}">
<th scope="row">Input props</th>
<td colspan="3">{{ profiler_dump(render.input_props) }}</td>
</tr>
<tr class="{{ not render.attributes|default ? 'opacity-50' }}">
<th scope="row">Attributes</th>
<td colspan="3">{{ profiler_dump(render.attributes) }}</td>
</tr>
<tr>
<th scope="row">Component</th>
<td colspan="3">{{ profiler_dump(render.component) }}</td>
</tr>
<tr class="{{ not render.input_props|default ? 'opacity-50' }}">
<th scope="row">Input props</th>
<td colspan="3">{{ profiler_dump(render.input_props) }}</td>
</tr>
<tr class="{{ not render.attributes|default ? 'opacity-50' }}">
<th scope="row">Attributes</th>
<td colspan="3">{{ profiler_dump(render.attributes) }}</td>
</tr>
<tr>
<th scope="row">Component</th>
<td colspan="3">{{ profiler_dump(render.component) }}</td>
</tr>
</tbody>
</table>
{% endfor %}
Expand Down

0 comments on commit 5eda833

Please sign in to comment.