Skip to content

Commit 23a7722

Browse files
committed
feature #3066 [TwigComponent][Perf] Add option to disable the dump of components (alinceDev, Kocal)
This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent][Perf] Add option to disable the dump of components | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Docs? | no | Issues | - | License | MIT This PR removes the component object dumping from the Symfony UX Twig Component profiler to address memory consumption issues. IMHO, the component dump is not necessary in the profiler as it can be quite verbose and memory-intensive. When detailed component inspection is needed, it's preferable to dump the component manually in the specific context where debugging is required. Commits ------- b1af221 [TwigComponent] Merge `profiler` and `profiler_collect_components` config options cb8f1a1 [TwigComponent][Perf] Add option to disable the dump of components
2 parents 9045a17 + b1af221 commit 23a7722

File tree

7 files changed

+81
-12
lines changed

7 files changed

+81
-12
lines changed

src/TwigComponent/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CHANGELOG
22

3+
## 2.32
4+
5+
- Add option `profiler.collect_components` to control component data collection
6+
in the profiler (enabled in debug mode by default)
7+
38
## 2.30
49

510
- Ensure compatibility with PHP 8.5

src/TwigComponent/config/debug.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\UX\TwigComponent\DataCollector\TwigComponentDataCollector;
1616
use Symfony\UX\TwigComponent\EventListener\TwigComponentLoggerListener;
1717

18+
use function Symfony\Component\DependencyInjection\Loader\Configurator\abstract_arg;
1819
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
1920

2021
return static function (ContainerConfigurator $container) {
@@ -27,6 +28,7 @@
2728
->args([
2829
service('ux.twig_component.component_logger_listener'),
2930
service('twig'),
31+
abstract_arg('profiler collect components'),
3032
])
3133
->tag('data_collector', [
3234
'template' => '@TwigComponent/Collector/twig_component.html.twig',

src/TwigComponent/src/DataCollector/TwigComponentDataCollector.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ final class TwigComponentDataCollector extends AbstractDataCollector implements
3535
public function __construct(
3636
private readonly TwigComponentLoggerListener $logger,
3737
private readonly Environment $twig,
38+
private readonly bool $collectComponents = true,
3839
) {
3940
$this->hasStub = class_exists(ClassStub::class);
4041
}
@@ -130,12 +131,15 @@ private function collectDataFromLogger(): void
130131
'input_props' => $mountedComponent->getInputProps(),
131132
'attributes' => $mountedComponent->getAttributes()->all(),
132133
'template_index' => $event->getTemplateIndex(),
133-
'component' => $mountedComponent->getComponent(),
134134
'depth' => \count($ongoingRenders),
135135
'children' => [],
136136
'render_start' => $profile[0],
137137
];
138138

139+
if ($this->collectComponents) {
140+
$renders[$renderId]['component'] = $mountedComponent->getComponent();
141+
}
142+
139143
if ($parentId = end($ongoingRenders)) {
140144
$renders[$parentId]['children'][] = $renderId;
141145
}

src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,11 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
150150
$container->setAlias('console.command.stimulus_component_debug', 'ux.twig_component.command.debug')
151151
->setDeprecated('symfony/ux-twig-component', '2.13', '%alias_id%');
152152

153-
if ($container->getParameter('kernel.debug') && $config['profiler']) {
153+
if ($config['profiler']['enabled']) {
154154
$loader->load('debug.php');
155+
156+
$container->getDefinition('ux.twig_component.data_collector')
157+
->setArgument(2, $config['profiler']['collect_components']);
155158
}
156159

157160
$loader->load('cache.php');
@@ -215,9 +218,13 @@ public function getConfigTreeBuilder(): TreeBuilder
215218
->scalarNode('anonymous_template_directory')
216219
->info('Defaults to `components`')
217220
->end()
218-
->booleanNode('profiler')
219-
->info('Enables the profiler for Twig Component (in debug mode)')
220-
->defaultValue('%kernel.debug%')
221+
->arrayNode('profiler')
222+
->info('Enables the profiler for Twig Component')
223+
->canBeEnabled()
224+
->children()
225+
->booleanNode('enabled')->defaultValue('%kernel.debug%')->end()
226+
->booleanNode('collect_components')->info('Collect components instances')->defaultTrue()->end()
227+
->end()
221228
->end()
222229
->scalarNode('controllers_json')
223230
->setDeprecated('symfony/ux-twig-component', '2.18', 'The "twig_component.controllers_json" config option is deprecated, and will be removed in 3.0.')

src/TwigComponent/templates/Collector/twig_component.html.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,12 @@
293293
<th scope="row">Attributes</th>
294294
<td colspan="4">{{ profiler_dump(render.attributes) }}</td>
295295
</tr>
296+
{% if render.component is defined %}
296297
<tr>
297298
<th scope="row">Component</th>
298299
<td colspan="4">{{ profiler_dump(render.component) }}</td>
299300
</tr>
301+
{% endif %}
300302
</tbody>
301303
</table>
302304
{% endfor %}

src/TwigComponent/tests/Unit/DataCollector/TwigComponentDataCollectorTest.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\UX\TwigComponent\ComponentAttributes;
18+
use Symfony\UX\TwigComponent\ComponentMetadata;
1719
use Symfony\UX\TwigComponent\DataCollector\TwigComponentDataCollector;
20+
use Symfony\UX\TwigComponent\Event\PostRenderEvent;
21+
use Symfony\UX\TwigComponent\Event\PreRenderEvent;
1822
use Symfony\UX\TwigComponent\EventListener\TwigComponentLoggerListener;
23+
use Symfony\UX\TwigComponent\MountedComponent;
1924
use Twig\Environment;
25+
use Twig\Loader\ArrayLoader;
26+
use Twig\Runtime\EscaperRuntime;
2027

2128
/**
2229
* @author Simon André <smn.andre@gmail.com>
@@ -35,7 +42,7 @@ public function testCollectDoesNothing()
3542
$this->assertSame([], $dataCollector->getData());
3643
}
3744

38-
public function testLateCollect()
45+
public function testLateCollectWithNoCollectedData()
3946
{
4047
$logger = new TwigComponentLoggerListener();
4148
$twig = $this->createMock(Environment::class);
@@ -54,6 +61,44 @@ public function testLateCollect()
5461
$this->assertEquals(0.0, $dataCollector->getRenderTime());
5562
}
5663

64+
/**
65+
* @testWith [true]
66+
* [false]
67+
*/
68+
public function testLateCollectWithCollectedData(bool $collectComponents)
69+
{
70+
$logger = new TwigComponentLoggerListener();
71+
$twig = new Environment(new ArrayLoader());
72+
$dataCollector = new TwigComponentDataCollector($logger, $twig, $collectComponents);
73+
74+
// Trigger some events to be logged
75+
$mounted = new MountedComponent('foo', new \stdClass(), new ComponentAttributes([], new EscaperRuntime()));
76+
$eventA = new PreRenderEvent($mounted, new ComponentMetadata(['key' => 'foo', 'template' => 'bar']), []);
77+
$logger->onPreRender($eventA);
78+
$eventB = new PostRenderEvent($mounted);
79+
$logger->onPostRender($eventB);
80+
81+
$dataCollector->lateCollect();
82+
83+
$this->assertSame(1, $dataCollector->getComponentCount());
84+
$this->assertIsIterable($dataCollector->getComponents());
85+
$this->assertNotEmpty($dataCollector->getComponents());
86+
87+
$this->assertSame(1, $dataCollector->getRenderCount());
88+
$this->assertIsIterable($dataCollector->getRenders());
89+
$this->assertNotEmpty($dataCollector->getRenders());
90+
91+
foreach ($dataCollector->getRenders() as $render) {
92+
if ($collectComponents) {
93+
$this->assertNotNull($render['component']);
94+
} else {
95+
$this->assertNull($render['component']);
96+
}
97+
}
98+
99+
$this->assertGreaterThan(0.0, $dataCollector->getRenderTime());
100+
}
101+
57102
public function testReset()
58103
{
59104
$logger = new TwigComponentLoggerListener();

src/TwigComponent/tests/Unit/DependencyInjection/TwigComponentExtensionTest.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,36 @@ public function testDataCollectorWithDebugMode()
3737
$this->compileContainer($container);
3838

3939
$this->assertTrue($container->hasDefinition('ux.twig_component.data_collector'));
40+
$this->assertTrue($container->getDefinition('ux.twig_component.data_collector')->getArgument(2));
4041
}
4142

42-
public function testDataCollectorWithDebugModeCanBeDisabled()
43+
public function testDataCollectorWithCollectComponentsDisabled()
4344
{
4445
$container = $this->createContainer();
4546
$container->setParameter('kernel.debug', true);
4647
$container->registerExtension(new TwigComponentExtension());
4748
$container->loadFromExtension('twig_component', [
4849
'defaults' => [],
4950
'anonymous_template_directory' => 'components/',
50-
'profiler' => false,
51+
'profiler' => [
52+
'collect_components' => false,
53+
],
5154
]);
5255
$this->compileContainer($container);
5356

54-
$this->assertFalse($container->hasDefinition('ux.twig_component.data_collector'));
57+
$this->assertTrue($container->hasDefinition('ux.twig_component.data_collector'));
58+
$this->assertFalse($container->getDefinition('ux.twig_component.data_collector')->getArgument(2));
5559
}
5660

57-
public function testDataCollectorWithoutDebugMode()
61+
public function testDataCollectorWithDebugModeCanBeDisabled()
5862
{
5963
$container = $this->createContainer();
60-
$container->setParameter('kernel.debug', false);
64+
$container->setParameter('kernel.debug', true);
6165
$container->registerExtension(new TwigComponentExtension());
6266
$container->loadFromExtension('twig_component', [
6367
'defaults' => [],
6468
'anonymous_template_directory' => 'components/',
65-
'profiler' => true,
69+
'profiler' => false,
6670
]);
6771
$this->compileContainer($container);
6872

0 commit comments

Comments
 (0)