Skip to content

Commit

Permalink
[Twig] Cache template class resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
smnandre committed Nov 11, 2024
1 parent 33ae8f2 commit 8623c61
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 35 deletions.
54 changes: 24 additions & 30 deletions src/TwigComponent/src/ComponentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@
*/
final class ComponentFactory implements ResetInterface
{
private static array $mountMethods = [];
private array $mountMethods = [];

/**
* @param array<string, array> $config
* @param array<class-string, string> $classMap
* @param array<class-string, array<string, string[]> $classMounts
*/
public function __construct(
private ComponentTemplateFinderInterface $componentTemplateFinder,
private ServiceLocator $components,
private PropertyAccessorInterface $propertyAccessor,
private EventDispatcherInterface $eventDispatcher,
private array $config,
private array $classMap,
private readonly array $classMap,
) {
}

Expand Down Expand Up @@ -88,22 +87,23 @@ public function create(string $name, array $data = []): MountedComponent
public function mountFromObject(object $component, array $data, ComponentMetadata $componentMetadata): MountedComponent
{
$originalData = $data;
$data = $this->preMount($component, $data, $componentMetadata);
$event = $this->preMount($component, $data, $componentMetadata);
$data = $event->getData();

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

// set data that wasn't set in mount on the component directly
foreach ($data as $property => $value) {
if ($this->propertyAccessor->isWritable($component, $property)) {
$this->propertyAccessor->setValue($component, $property, $value);

unset($data[$property]);
if (!$componentMetadata->isAnonymous()) {
// set data that wasn't set in mount on the component directly
foreach ($data as $property => $value) {
if ($this->propertyAccessor->isWritable($component, $property)) {
$this->propertyAccessor->setValue($component, $property, $value);
unset($data[$property]);
}
}
}

$postMount = $this->postMount($component, $data, $componentMetadata);
$data = $postMount['data'];
$extraMetadata = $postMount['extraMetadata'];
$data = $postMount->getData();

// create attributes from "attributes" key if exists
$attributesVar = $componentMetadata->getAttributesVar();
Expand All @@ -120,9 +120,9 @@ public function mountFromObject(object $component, array $data, ComponentMetadat
return new MountedComponent(
$componentMetadata->getName(),
$component,
new ComponentAttributes(array_merge($attributes, $data)),
new ComponentAttributes([...$attributes, ...$data]),
$originalData,
$extraMetadata,
$postMount->getExtraMetadata(),
);
}

Expand Down Expand Up @@ -154,7 +154,7 @@ private function mount(object $component, array &$data, ComponentMetadata $compo
return;
}

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

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

private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): PreMountEvent
{
$event = new PreMountEvent($component, $data, $componentMetadata);
$this->eventDispatcher->dispatch($event);
$data = $event->getData();

$data = $event->getData();
foreach ($componentMetadata->getPreMounts() as $preMount) {
if (null !== $newData = $component->$preMount($data)) {
$data = $newData;
$event->setData($data = $newData);
}
}

return $data;
return $event;
}

/**
* @return array{data: array<string, mixed>, extraMetadata: array<string, mixed>}
*/
private function postMount(object $component, array $data, ComponentMetadata $componentMetadata): array
private function postMount(object $component, array $data, ComponentMetadata $componentMetadata): PostMountEvent
{
$event = new PostMountEvent($component, $data, $componentMetadata);
$this->eventDispatcher->dispatch($event);
$data = $event->getData();

$data = $event->getData();
foreach ($componentMetadata->getPostMounts() as $postMount) {
if (null !== $newData = $component->$postMount($data)) {
$data = $newData;
$event->setData($data = $newData);
}
}

return [
'data' => $data,
'extraMetadata' => $event->getExtraMetadata(),
];
return $event;
}

/**
Expand Down Expand Up @@ -244,6 +238,6 @@ private function throwUnknownComponentException(string $name): void

public function reset(): void
{
self::$mountMethods = [];
$this->mountMethods = [];
}
}
18 changes: 13 additions & 5 deletions src/TwigComponent/src/ComponentRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\UX\TwigComponent;

use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\ResetInterface;
use Symfony\UX\TwigComponent\Event\PostRenderEvent;
use Symfony\UX\TwigComponent\Event\PreCreateForRenderEvent;
use Symfony\UX\TwigComponent\Event\PreRenderEvent;
Expand All @@ -22,8 +23,10 @@
*
* @internal
*/
final class ComponentRenderer implements ComponentRendererInterface
final class ComponentRenderer implements ComponentRendererInterface, ResetInterface
{
private array $templateClasses = [];

public function __construct(
private Environment $twig,
private EventDispatcherInterface $dispatcher,
Expand Down Expand Up @@ -62,15 +65,15 @@ public function render(MountedComponent $mounted): string
$variables = $event->getVariables();
// see ComponentNode. When rendering an individual embedded component,
// *not* through its parent, we need to set the parent template.
if ($event->getTemplateIndex()) {
if ($templateIndex = $event->getTemplateIndex()) {
$variables['__parent__'] = $event->getParentTemplateForEmbedded();
}

try {
return $this->twig->loadTemplate(
$this->twig->getTemplateClass($event->getTemplate()),
$event->getTemplate(),
$event->getTemplateIndex(),
$this->templateClasses[$template = $event->getTemplate()] ??= $this->twig->getTemplateClass($template),
$template,
$templateIndex,
)->render($variables);
} finally {
$mounted = $this->componentStack->pop();
Expand Down Expand Up @@ -137,4 +140,9 @@ private function preRender(MountedComponent $mounted, array $context = []): PreR

return $event;
}

public function reset(): void
{
$this->templateClasses = [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
new Reference('ux.twig_component.component_properties'),
new Reference('ux.twig_component.component_stack'),
])
->addTag('kernel.reset', ['method' => 'reset'])
;

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

0 comments on commit 8623c61

Please sign in to comment.