Skip to content

Commit 8623c61

Browse files
committed
[Twig] Cache template class resolution
1 parent 33ae8f2 commit 8623c61

File tree

3 files changed

+38
-35
lines changed

3 files changed

+38
-35
lines changed

src/TwigComponent/src/ComponentFactory.php

Lines changed: 24 additions & 30 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,22 +87,23 @@ 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();
@@ -120,9 +120,9 @@ public function mountFromObject(object $component, array $data, ComponentMetadat
120120
return new MountedComponent(
121121
$componentMetadata->getName(),
122122
$component,
123-
new ComponentAttributes(array_merge($attributes, $data)),
123+
new ComponentAttributes([...$attributes, ...$data]),
124124
$originalData,
125-
$extraMetadata,
125+
$postMount->getExtraMetadata(),
126126
);
127127
}
128128

@@ -154,7 +154,7 @@ private function mount(object $component, array &$data, ComponentMetadata $compo
154154
return;
155155
}
156156

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

159159
$parameters = [];
160160
foreach ($mount->getParameters() as $refParameter) {
@@ -172,40 +172,34 @@ private function mount(object $component, array &$data, ComponentMetadata $compo
172172
$mount->invoke($component, ...$parameters);
173173
}
174174

175-
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
175+
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): PreMountEvent
176176
{
177177
$event = new PreMountEvent($component, $data, $componentMetadata);
178178
$this->eventDispatcher->dispatch($event);
179-
$data = $event->getData();
180179

180+
$data = $event->getData();
181181
foreach ($componentMetadata->getPreMounts() as $preMount) {
182182
if (null !== $newData = $component->$preMount($data)) {
183-
$data = $newData;
183+
$event->setData($data = $newData);
184184
}
185185
}
186186

187-
return $data;
187+
return $event;
188188
}
189189

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

195+
$data = $event->getData();
199196
foreach ($componentMetadata->getPostMounts() as $postMount) {
200197
if (null !== $newData = $component->$postMount($data)) {
201-
$data = $newData;
198+
$event->setData($data = $newData);
202199
}
203200
}
204201

205-
return [
206-
'data' => $data,
207-
'extraMetadata' => $event->getExtraMetadata(),
208-
];
202+
return $event;
209203
}
210204

211205
/**
@@ -244,6 +238,6 @@ private function throwUnknownComponentException(string $name): void
244238

245239
public function reset(): void
246240
{
247-
self::$mountMethods = [];
241+
$this->mountMethods = [];
248242
}
249243
}

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)