Skip to content

Commit 8a9f8b5

Browse files
committed
[TypeInfo] Add result cache to TypeContextFactory
Add a cache to method with the aims to optimize deserialization and form validation. Closes #61725
1 parent 1ed06ec commit 8a9f8b5

File tree

2 files changed

+54
-25
lines changed

2 files changed

+54
-25
lines changed

src/Symfony/Component/TypeInfo/Tests/TypeContext/TypeContextFactoryTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ public function testCollectClassNames()
6060
$this->assertSame('Dummy', $typeContext->declaringClassName);
6161
}
6262

63+
public function testCacheResultWhenToStringTypeResolver()
64+
{
65+
$typeContext = $this->typeContextFactory->createFromClassName(Dummy::class, AbstractDummy::class);
66+
$cachedtypeContext = $this->typeContextFactory->createFromClassName(Dummy::class, AbstractDummy::class);
67+
$this->assertSame($typeContext, $cachedtypeContext);
68+
}
69+
6370
public function testCollectNamespace()
6471
{
6572
$namespace = 'Symfony\\Component\\TypeInfo\\Tests\\Fixtures';

src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*
3434
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
3535
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
36+
* @author Pierre-Yves Landuré <landure@gmail.com>
3637
*/
3738
final class TypeContextFactory
3839
{
@@ -41,6 +42,16 @@ final class TypeContextFactory
4142
*/
4243
private static array $reflectionClassCache = [];
4344

45+
/**
46+
* @var array<string,array<string,TypeContext>
47+
*/
48+
private array $intermediateTypeContextCache = [];
49+
50+
/**
51+
* @var array<string,array<string,TypeContext>
52+
*/
53+
private array $typeContextCache = [];
54+
4455
private ?Lexer $phpstanLexer = null;
4556
private ?PhpDocParser $phpstanParser = null;
4657

@@ -53,26 +64,9 @@ public function createFromClassName(string $calledClassName, ?string $declaringC
5364
{
5465
$declaringClassName ??= $calledClassName;
5566

56-
$calledClassPath = explode('\\', $calledClassName);
57-
$declaringClassPath = explode('\\', $declaringClassName);
58-
59-
$declaringClassReflection = self::$reflectionClassCache[$declaringClassName] ??= new \ReflectionClass($declaringClassName);
67+
$this->typeContextCache[$declaringClassName][$calledClassName] ??= $this->createNewInstanceFromClassName($calledClassName, $declaringClassName);
6068

61-
$typeContext = new TypeContext(
62-
end($calledClassPath),
63-
end($declaringClassPath),
64-
trim($declaringClassReflection->getNamespaceName(), '\\'),
65-
$this->collectUses($declaringClassReflection),
66-
);
67-
68-
return new TypeContext(
69-
$typeContext->calledClassName,
70-
$typeContext->declaringClassName,
71-
$typeContext->namespace,
72-
$typeContext->uses,
73-
$this->collectTemplates($declaringClassReflection, $typeContext),
74-
$this->collectTypeAliases($declaringClassReflection, $typeContext),
75-
);
69+
return $this->typeContextCache[$declaringClassName][$calledClassName];
7670
}
7771

7872
public function createFromReflection(\Reflector $reflection): ?TypeContext
@@ -90,12 +84,7 @@ public function createFromReflection(\Reflector $reflection): ?TypeContext
9084
return null;
9185
}
9286

93-
$typeContext = new TypeContext(
94-
$declaringClassReflection->getShortName(),
95-
$declaringClassReflection->getShortName(),
96-
$declaringClassReflection->getNamespaceName(),
97-
$this->collectUses($declaringClassReflection),
98-
);
87+
$typeContext = $this->createIntermediateTypeContext($declaringClassReflection->getShortName(), $declaringClassReflection);
9988

10089
$templates = match (true) {
10190
$reflection instanceof \ReflectionFunctionAbstract => $this->collectTemplates($reflection, $typeContext) + $this->collectTemplates($declaringClassReflection, $typeContext),
@@ -113,6 +102,39 @@ public function createFromReflection(\Reflector $reflection): ?TypeContext
113102
);
114103
}
115104

105+
private function createNewInstanceFromClassName(string $calledClassName, string $declaringClassName): TypeContext
106+
{
107+
$calledClassPath = explode('\\', $calledClassName);
108+
$declaringClassPath = explode('\\', $declaringClassName);
109+
110+
$declaringClassReflection = self::$reflectionClassCache[$declaringClassName] ??= new \ReflectionClass($declaringClassName);
111+
112+
$typeContext = $this->createIntermediateTypeContext(end($calledClassPath), $declaringClassReflection);
113+
114+
return new TypeContext(
115+
$typeContext->calledClassName,
116+
$typeContext->declaringClassName,
117+
$typeContext->namespace,
118+
$typeContext->uses,
119+
$this->collectTemplates($declaringClassReflection, $typeContext),
120+
$this->collectTypeAliases($declaringClassReflection, $typeContext),
121+
);
122+
}
123+
124+
private function createIntermediateTypeContext(string $calledClassShortName, \ReflectionClass $declaringClassReflection): TypeContext
125+
{
126+
$declaringClassName = $declaringClassReflection->getName();
127+
128+
self::$intermediateTypeContextCache[$declaringClassName][$calledClassShortName] ??= new TypeContext(
129+
$calledClassShortName,
130+
$declaringClassReflection->getShortName(),
131+
trim($declaringClassReflection->getNamespaceName(), '\\'),
132+
$this->collectUses($declaringClassReflection),
133+
);
134+
135+
return self::$intermediateTypeContextCache[$declaringClassName][$calledClassShortName];
136+
}
137+
116138
/**
117139
* @return array<string, string>
118140
*/

0 commit comments

Comments
 (0)