Skip to content

Commit 7dff0df

Browse files
committed
minor #2344 [TwigComponent] Improve ComponentFactory performances (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Improve ComponentFactory performances ComponentFactory Quick Optimization Minor change with.. mid impact (on large number of renders) Commits ------- 6486933 [TwigComponent] Improve ComponentFactory performances
2 parents 3a1ae4e + 6486933 commit 7dff0df

File tree

3 files changed

+39
-26
lines changed

3 files changed

+39
-26
lines changed

src/TwigComponent/src/ComponentFactory.php

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Psr\EventDispatcher\EventDispatcherInterface;
1515
use Symfony\Component\DependencyInjection\ServiceLocator;
1616
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
17+
use Symfony\Contracts\Service\ResetInterface;
1718
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
1819
use Symfony\UX\TwigComponent\Event\PostMountEvent;
1920
use Symfony\UX\TwigComponent\Event\PreMountEvent;
@@ -23,8 +24,12 @@
2324
*
2425
* @internal
2526
*/
26-
final class ComponentFactory
27+
final class ComponentFactory implements ResetInterface
2728
{
29+
private static $mountMethods = [];
30+
private static $preMountMethods = [];
31+
private static $postMountMethods = [];
32+
2833
/**
2934
* @param array<string, array> $config
3035
* @param array<class-string, string> $classMap
@@ -141,37 +146,40 @@ public function get(string $name): object
141146

142147
private function mount(object $component, array &$data): void
143148
{
144-
try {
145-
$method = (new \ReflectionClass($component))->getMethod('mount');
146-
} catch (\ReflectionException) {
147-
// no hydrate method
148-
return;
149-
}
150-
151149
if ($component instanceof AnonymousComponent) {
152150
$component->mount($data);
153151

154152
return;
155153
}
156154

157-
$parameters = [];
155+
if (null === (self::$mountMethods[$component::class] ?? null)) {
156+
try {
157+
$mountMethod = self::$mountMethods[$component::class] = (new \ReflectionClass($component))->getMethod('mount');
158+
} catch (\ReflectionException) {
159+
self::$mountMethods[$component::class] = false;
158160

159-
foreach ($method->getParameters() as $refParameter) {
160-
$name = $refParameter->getName();
161+
return;
162+
}
163+
}
161164

162-
if (\array_key_exists($name, $data)) {
163-
$parameters[] = $data[$name];
165+
if (false === $mountMethod ??= self::$mountMethods[$component::class]) {
166+
return;
167+
}
164168

169+
$parameters = [];
170+
foreach ($mountMethod->getParameters() as $refParameter) {
171+
if (\array_key_exists($name = $refParameter->getName(), $data)) {
172+
$parameters[] = $data[$name];
165173
// remove the data element so it isn't used to set the property directly.
166174
unset($data[$name]);
167175
} elseif ($refParameter->isDefaultValueAvailable()) {
168176
$parameters[] = $refParameter->getDefaultValue();
169177
} else {
170-
throw new \LogicException(\sprintf('%s::mount() has a required $%s parameter. Make sure this is passed or make give a default value.', $component::class, $refParameter->getName()));
178+
throw new \LogicException(\sprintf('%s::mount() has a required $%s parameter. Make sure to pass it or give it a default value.', $component::class, $name));
171179
}
172180
}
173181

174-
$component->mount(...$parameters);
182+
$mountMethod->invoke($component, ...$parameters);
175183
}
176184

177185
private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
@@ -180,10 +188,9 @@ private function preMount(object $component, array $data, ComponentMetadata $com
180188
$this->eventDispatcher->dispatch($event);
181189
$data = $event->getData();
182190

183-
foreach (AsTwigComponent::preMountMethods($component) as $method) {
184-
$newData = $component->{$method->name}($data);
185-
186-
if (null !== $newData) {
191+
$methods = self::$preMountMethods[$component::class] ??= AsTwigComponent::preMountMethods($component::class);
192+
foreach ($methods as $method) {
193+
if (null !== $newData = $method->invoke($component, $data)) {
187194
$data = $newData;
188195
}
189196
}
@@ -199,19 +206,17 @@ private function postMount(object $component, array $data, ComponentMetadata $co
199206
$event = new PostMountEvent($component, $data, $componentMetadata);
200207
$this->eventDispatcher->dispatch($event);
201208
$data = $event->getData();
202-
$extraMetadata = $event->getExtraMetadata();
203209

204-
foreach (AsTwigComponent::postMountMethods($component) as $method) {
205-
$newData = $component->{$method->name}($data);
206-
207-
if (null !== $newData) {
210+
$methods = self::$postMountMethods[$component::class] ??= AsTwigComponent::postMountMethods($component::class);
211+
foreach ($methods as $method) {
212+
if (null !== $newData = $method->invoke($component, $data)) {
208213
$data = $newData;
209214
}
210215
}
211216

212217
return [
213218
'data' => $data,
214-
'extraMetadata' => $extraMetadata,
219+
'extraMetadata' => $event->getExtraMetadata(),
215220
];
216221
}
217222

@@ -248,4 +253,11 @@ private function throwUnknownComponentException(string $name): void
248253

249254
throw new \InvalidArgumentException($message);
250255
}
256+
257+
public function reset(): void
258+
{
259+
self::$mountMethods = [];
260+
self::$preMountMethods = [];
261+
self::$postMountMethods = [];
262+
}
251263
}

src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
9292
new Reference('event_dispatcher'),
9393
new AbstractArgument(\sprintf('Added in %s.', TwigComponentPass::class)),
9494
])
95+
->addTag('kernel.reset', ['method' => 'reset'])
9596
;
9697

9798
$container->register('ux.twig_component.component_stack', ComponentStack::class);

src/TwigComponent/tests/Integration/ComponentFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function testMountCanHaveOptionalParameters(): void
8787
public function testExceptionThrownIfRequiredMountParameterIsMissingFromPassedData(): void
8888
{
8989
$this->expectException(\LogicException::class);
90-
$this->expectExceptionMessage('Symfony\UX\TwigComponent\Tests\Fixtures\Component\ComponentC::mount() has a required $propA parameter. Make sure this is passed or make give a default value.');
90+
$this->expectExceptionMessage('Symfony\UX\TwigComponent\Tests\Fixtures\Component\ComponentC::mount() has a required $propA parameter. Make sure to pass it or give it a default value.');
9191

9292
$this->createComponent('component_c');
9393
}

0 commit comments

Comments
 (0)