Skip to content

Commit 5a1c8cd

Browse files
ondrejmirtesclaude
andcommitted
Drop the container reference in LazyClassReflectionExtensionRegistryProvider after building the registry
Every ClassReflection instance holds this provider as a constructor dependency. Because the provider kept its Container reference for the whole process, every ClassReflection — and transitively every ObjectType that ever resolved its class, every ResolvedPhpDocBlock tag type, every cached signature — was a handle on the entire DI container. That reference is only needed once, to build the registry lazily. Null it out afterwards. Verified with retention-path instrumentation on a large project: no reference chain from ClassReflection to the container remains, which is a prerequisite for any future eviction of class reflections to actually free memory (and prevents a stale reflection from keeping a dead container alive, e.g. after stub validation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NrTvR9j1mW2NNNC8RkuTsF
1 parent 8aea813 commit 5a1c8cd

1 file changed

Lines changed: 24 additions & 13 deletions

File tree

‎src/DependencyInjection/Reflection/LazyClassReflectionExtensionRegistryProvider.php‎

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension;
1616
use PHPStan\Reflection\RequireExtension\RequireExtendsMethodsClassReflectionExtension;
1717
use PHPStan\Reflection\RequireExtension\RequireExtendsPropertiesClassReflectionExtension;
18+
use PHPStan\ShouldNotHappenException;
1819
use function array_merge;
1920

2021
#[AutowiredService(as: ClassReflectionExtensionRegistryProvider::class)]
@@ -23,29 +24,39 @@ final class LazyClassReflectionExtensionRegistryProvider implements ClassReflect
2324

2425
private ?ClassReflectionExtensionRegistry $registry = null;
2526

26-
public function __construct(private Container $container)
27+
public function __construct(private ?Container $container)
2728
{
2829
}
2930

3031
public function getRegistry(): ClassReflectionExtensionRegistry
3132
{
3233
if ($this->registry === null) {
33-
$annotationsMethodsClassReflectionExtension = $this->container->getByType(AnnotationsMethodsClassReflectionExtension::class);
34-
$annotationsPropertiesClassReflectionExtension = $this->container->getByType(AnnotationsPropertiesClassReflectionExtension::class);
34+
$container = $this->container;
35+
if ($container === null) {
36+
throw new ShouldNotHappenException();
37+
}
3538

36-
$mixinMethodsClassReflectionExtension = $this->container->getByType(MixinMethodsClassReflectionExtension::class);
37-
$mixinPropertiesClassReflectionExtension = $this->container->getByType(MixinPropertiesClassReflectionExtension::class);
38-
$soapClientMethodsClassReflectionExtension = $this->container->getByType(SoapClientMethodsClassReflectionExtension::class);
39-
$universalObjectCratesClassReflectionExtension = $this->container->getByType(UniversalObjectCratesClassReflectionExtension::class);
39+
$annotationsMethodsClassReflectionExtension = $container->getByType(AnnotationsMethodsClassReflectionExtension::class);
40+
$annotationsPropertiesClassReflectionExtension = $container->getByType(AnnotationsPropertiesClassReflectionExtension::class);
41+
42+
$mixinMethodsClassReflectionExtension = $container->getByType(MixinMethodsClassReflectionExtension::class);
43+
$mixinPropertiesClassReflectionExtension = $container->getByType(MixinPropertiesClassReflectionExtension::class);
44+
$soapClientMethodsClassReflectionExtension = $container->getByType(SoapClientMethodsClassReflectionExtension::class);
45+
$universalObjectCratesClassReflectionExtension = $container->getByType(UniversalObjectCratesClassReflectionExtension::class);
4046

4147
$this->registry = new ClassReflectionExtensionRegistry(
42-
array_merge($this->container->getServicesByTag(BrokerFactory::PROPERTIES_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsPropertiesClassReflectionExtension, $mixinPropertiesClassReflectionExtension, $universalObjectCratesClassReflectionExtension]),
43-
array_merge($this->container->getServicesByTag(BrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsMethodsClassReflectionExtension, $mixinMethodsClassReflectionExtension, $soapClientMethodsClassReflectionExtension]),
44-
$this->container->getServicesByTag(BrokerFactory::ALLOWED_SUB_TYPES_CLASS_REFLECTION_EXTENSION_TAG),
45-
$this->container->getByType(RequireExtendsPropertiesClassReflectionExtension::class),
46-
$this->container->getByType(RequireExtendsMethodsClassReflectionExtension::class),
47-
$this->container->getByType(PhpClassReflectionExtension::class),
48+
array_merge($container->getServicesByTag(BrokerFactory::PROPERTIES_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsPropertiesClassReflectionExtension, $mixinPropertiesClassReflectionExtension, $universalObjectCratesClassReflectionExtension]),
49+
array_merge($container->getServicesByTag(BrokerFactory::METHODS_CLASS_REFLECTION_EXTENSION_TAG), [$annotationsMethodsClassReflectionExtension, $mixinMethodsClassReflectionExtension, $soapClientMethodsClassReflectionExtension]),
50+
$container->getServicesByTag(BrokerFactory::ALLOWED_SUB_TYPES_CLASS_REFLECTION_EXTENSION_TAG),
51+
$container->getByType(RequireExtendsPropertiesClassReflectionExtension::class),
52+
$container->getByType(RequireExtendsMethodsClassReflectionExtension::class),
53+
$container->getByType(PhpClassReflectionExtension::class),
4854
);
55+
56+
// Every ClassReflection instance holds this provider; keeping the container
57+
// reference here would make each of them a transitive handle on the entire DI
58+
// container. After the registry is built the container is no longer needed.
59+
$this->container = null;
4960
}
5061

5162
return $this->registry;

0 commit comments

Comments
 (0)