Skip to content

Commit 89c7fa9

Browse files
committed
feature #2364 [Twig] Cache template class resolution (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Twig] Cache template class resolution Cache template class during rendering. Non negligeable impact on pages with many components. <img width="806" alt="Capture d’écran 2024-11-11 à 01 59 30" src="https://github.com/user-attachments/assets/8604c359-11f2-4482-aacc-8670e5ce5006"> Commits ------- 75a2997 [Twig] Cache template class resolution
2 parents 0c71f97 + 75a2997 commit 89c7fa9

File tree

4 files changed

+39
-36
lines changed

4 files changed

+39
-36
lines changed

src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,7 @@ public function getTest(LiveComponentMetadataFactory $metadataFactory): Hydratio
18751875
new ComponentMetadata([
18761876
'key' => '__testing',
18771877
'mount' => $reflectionClass->hasMethod('mount') ? ['mount'] : [],
1878+
'service_id' => '__testing_id',
18781879
]),
18791880
$metadataFactory->createPropMetadatas($reflectionClass),
18801881
),

src/TwigComponent/src/ComponentFactory.php

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,19 @@
2525
*/
2626
final class ComponentFactory implements ResetInterface
2727
{
28-
private static array $mountMethods = [];
28+
private array $mountMethods = [];
2929

3030
/**
3131
* @param array<string, array> $config
3232
* @param array<class-string, string> $classMap
33-
* @param array<class-string, array<string, string[]> $classMounts
3433
*/
3534
public function __construct(
3635
private ComponentTemplateFinderInterface $componentTemplateFinder,
3736
private ServiceLocator $components,
3837
private PropertyAccessorInterface $propertyAccessor,
3938
private EventDispatcherInterface $eventDispatcher,
4039
private array $config,
41-
private array $classMap,
40+
private readonly array $classMap,
4241
) {
4342
}
4443

@@ -88,29 +87,29 @@ public function create(string $name, array $data = []): MountedComponent
8887
public function mountFromObject(object $component, array $data, ComponentMetadata $componentMetadata): MountedComponent
8988
{
9089
$originalData = $data;
91-
$data = $this->preMount($component, $data, $componentMetadata);
90+
$event = $this->preMount($component, $data, $componentMetadata);
91+
$data = $event->getData();
9292

9393
$this->mount($component, $data, $componentMetadata);
9494

95-
// set data that wasn't set in mount on the component directly
96-
foreach ($data as $property => $value) {
97-
if ($this->propertyAccessor->isWritable($component, $property)) {
98-
$this->propertyAccessor->setValue($component, $property, $value);
99-
100-
unset($data[$property]);
95+
if (!$componentMetadata->isAnonymous()) {
96+
// set data that wasn't set in mount on the component directly
97+
foreach ($data as $property => $value) {
98+
if ($this->propertyAccessor->isWritable($component, $property)) {
99+
$this->propertyAccessor->setValue($component, $property, $value);
100+
unset($data[$property]);
101+
}
101102
}
102103
}
103104

104105
$postMount = $this->postMount($component, $data, $componentMetadata);
105-
$data = $postMount['data'];
106-
$extraMetadata = $postMount['extraMetadata'];
106+
$data = $postMount->getData();
107107

108108
// create attributes from "attributes" key if exists
109109
$attributesVar = $componentMetadata->getAttributesVar();
110110
$attributes = $data[$attributesVar] ?? [];
111111
unset($data[$attributesVar]);
112112

113-
// ensure remaining data is scalar
114113
foreach ($data as $key => $value) {
115114
if ($value instanceof \Stringable) {
116115
$data[$key] = (string) $value;
@@ -120,9 +119,9 @@ public function mountFromObject(object $component, array $data, ComponentMetadat
120119
return new MountedComponent(
121120
$componentMetadata->getName(),
122121
$component,
123-
new ComponentAttributes(array_merge($attributes, $data)),
122+
new ComponentAttributes([...$attributes, ...$data]),
124123
$originalData,
125-
$extraMetadata,
124+
$postMount->getExtraMetadata(),
126125
);
127126
}
128127

@@ -154,7 +153,7 @@ private function mount(object $component, array &$data, ComponentMetadata $compo
154153
return;
155154
}
156155

157-
$mount = self::$mountMethods[$component::class] ??= (new \ReflectionClass($component))->getMethod('mount');
156+
$mount = $this->mountMethods[$component::class] ??= (new \ReflectionClass($component))->getMethod('mount');
158157

159158
$parameters = [];
160159
foreach ($mount->getParameters() as $refParameter) {
@@ -172,40 +171,34 @@ private function mount(object $component, array &$data, ComponentMetadata $compo
172171
$mount->invoke($component, ...$parameters);
173172
}
174173

175-
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
174+
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): PreMountEvent
176175
{
177176
$event = new PreMountEvent($component, $data, $componentMetadata);
178177
$this->eventDispatcher->dispatch($event);
179-
$data = $event->getData();
180178

179+
$data = $event->getData();
181180
foreach ($componentMetadata->getPreMounts() as $preMount) {
182181
if (null !== $newData = $component->$preMount($data)) {
183-
$data = $newData;
182+
$event->setData($data = $newData);
184183
}
185184
}
186185

187-
return $data;
186+
return $event;
188187
}
189188

190-
/**
191-
* @return array{data: array<string, mixed>, extraMetadata: array<string, mixed>}
192-
*/
193-
private function postMount(object $component, array $data, ComponentMetadata $componentMetadata): array
189+
private function postMount(object $component, array $data, ComponentMetadata $componentMetadata): PostMountEvent
194190
{
195191
$event = new PostMountEvent($component, $data, $componentMetadata);
196192
$this->eventDispatcher->dispatch($event);
197-
$data = $event->getData();
198193

194+
$data = $event->getData();
199195
foreach ($componentMetadata->getPostMounts() as $postMount) {
200196
if (null !== $newData = $component->$postMount($data)) {
201-
$data = $newData;
197+
$event->setData($data = $newData);
202198
}
203199
}
204200

205-
return [
206-
'data' => $data,
207-
'extraMetadata' => $event->getExtraMetadata(),
208-
];
201+
return $event;
209202
}
210203

211204
/**
@@ -244,6 +237,6 @@ private function throwUnknownComponentException(string $name): void
244237

245238
public function reset(): void
246239
{
247-
self::$mountMethods = [];
240+
$this->mountMethods = [];
248241
}
249242
}

src/TwigComponent/src/ComponentRenderer.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\UX\TwigComponent;
1313

1414
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
15+
use Symfony\Contracts\Service\ResetInterface;
1516
use Symfony\UX\TwigComponent\Event\PostRenderEvent;
1617
use Symfony\UX\TwigComponent\Event\PreCreateForRenderEvent;
1718
use Symfony\UX\TwigComponent\Event\PreRenderEvent;
@@ -22,8 +23,10 @@
2223
*
2324
* @internal
2425
*/
25-
final class ComponentRenderer implements ComponentRendererInterface
26+
final class ComponentRenderer implements ComponentRendererInterface, ResetInterface
2627
{
28+
private array $templateClasses = [];
29+
2730
public function __construct(
2831
private Environment $twig,
2932
private EventDispatcherInterface $dispatcher,
@@ -62,15 +65,15 @@ public function render(MountedComponent $mounted): string
6265
$variables = $event->getVariables();
6366
// see ComponentNode. When rendering an individual embedded component,
6467
// *not* through its parent, we need to set the parent template.
65-
if ($event->getTemplateIndex()) {
68+
if ($templateIndex = $event->getTemplateIndex()) {
6669
$variables['__parent__'] = $event->getParentTemplateForEmbedded();
6770
}
6871

6972
try {
7073
return $this->twig->loadTemplate(
71-
$this->twig->getTemplateClass($event->getTemplate()),
72-
$event->getTemplate(),
73-
$event->getTemplateIndex(),
74+
$this->templateClasses[$template = $event->getTemplate()] ??= $this->twig->getTemplateClass($template),
75+
$template,
76+
$templateIndex,
7477
)->render($variables);
7578
} finally {
7679
$mounted = $this->componentStack->pop();
@@ -137,4 +140,9 @@ private function preRender(MountedComponent $mounted, array $context = []): PreR
137140

138141
return $event;
139142
}
143+
144+
public function reset(): void
145+
{
146+
$this->templateClasses = [];
147+
}
140148
}

src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
113113
new Reference('ux.twig_component.component_properties'),
114114
new Reference('ux.twig_component.component_stack'),
115115
])
116+
->addTag('kernel.reset', ['method' => 'reset'])
116117
;
117118

118119
$container->register('ux.twig_component.twig.component_extension', ComponentExtension::class)

0 commit comments

Comments
 (0)