1212namespace Symfony \Bridge \Doctrine \Tests ;
1313
1414use PHPUnit \Framework \TestCase ;
15+ use ProxyManager \Proxy \LazyLoadingInterface ;
16+ use ProxyManager \Proxy \ValueHolderInterface ;
1517use Symfony \Bridge \Doctrine \ManagerRegistry ;
18+ use Symfony \Bridge \ProxyManager \LazyProxy \PhpDumper \ProxyDumper ;
1619use Symfony \Bridge \ProxyManager \Tests \LazyProxy \Dumper \PhpDumperTest ;
20+ use Symfony \Component \DependencyInjection \ContainerBuilder ;
21+ use Symfony \Component \DependencyInjection \ContainerInterface ;
22+ use Symfony \Component \DependencyInjection \Dumper \PhpDumper ;
23+ use Symfony \Component \Filesystem \Filesystem ;
1724
1825class ManagerRegistryTest extends TestCase
1926{
@@ -39,6 +46,92 @@ public function testResetService()
3946 $ this ->assertSame ($ foo , $ container ->get ('foo ' ));
4047 $ this ->assertObjectNotHasAttribute ('bar ' , $ foo );
4148 }
49+
50+ /**
51+ * When performing an entity manager lazy service reset, the reset operations may re-use the container
52+ * to create a "fresh" service: when doing so, it can happen that the "fresh" service is itself a proxy.
53+ *
54+ * Because of that, the proxy will be populated with a wrapped value that is itself a proxy: repeating
55+ * the reset operation keeps increasing this nesting until the application eventually runs into stack
56+ * overflow or memory overflow operations, which can happen for long-running processes that rely on
57+ * services that are reset very often.
58+ */
59+ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther ()
60+ {
61+ // This test scenario only applies to containers composed as a set of generated sources
62+ $ this ->dumpLazyServiceProjectAsFilesServiceContainer ();
63+
64+ /** @var ContainerInterface $container */
65+ $ container = new \LazyServiceProjectAsFilesServiceContainer ();
66+
67+ $ registry = new TestManagerRegistry (
68+ 'irrelevant ' ,
69+ [],
70+ ['defaultManager ' => 'foo ' ],
71+ 'irrelevant ' ,
72+ 'defaultManager ' ,
73+ 'irrelevant '
74+ );
75+ $ registry ->setTestContainer ($ container );
76+
77+ $ service = $ container ->get ('foo ' );
78+
79+ self ::assertInstanceOf (\stdClass::class, $ service );
80+ self ::assertInstanceOf (LazyLoadingInterface::class, $ service );
81+ self ::assertInstanceOf (ValueHolderInterface::class, $ service );
82+ self ::assertFalse ($ service ->isProxyInitialized ());
83+
84+ $ service ->initializeProxy ();
85+
86+ self ::assertTrue ($ container ->initialized ('foo ' ));
87+ self ::assertTrue ($ service ->isProxyInitialized ());
88+
89+ $ registry ->resetManager ();
90+ $ service ->initializeProxy ();
91+
92+ $ wrappedValue = $ service ->getWrappedValueHolderValue ();
93+ self ::assertInstanceOf (\stdClass::class, $ wrappedValue );
94+ self ::assertNotInstanceOf (LazyLoadingInterface::class, $ wrappedValue );
95+ self ::assertNotInstanceOf (ValueHolderInterface::class, $ wrappedValue );
96+ }
97+
98+ /** @return void */
99+ private function dumpLazyServiceProjectAsFilesServiceContainer ()
100+ {
101+ if (class_exists (\LazyServiceProjectAsFilesServiceContainer::class, false )) {
102+ return ;
103+ }
104+
105+ $ container = new ContainerBuilder ();
106+
107+ $ container ->register ('foo ' , \stdClass::class)
108+ ->setPublic (true )
109+ ->setLazy (true );
110+ $ container ->compile ();
111+
112+ $ fileSystem = new Filesystem ();
113+
114+ $ temporaryPath = $ fileSystem ->tempnam (sys_get_temp_dir (), 'symfonyManagerRegistryTest ' );
115+ $ fileSystem ->remove ($ temporaryPath );
116+ $ fileSystem ->mkdir ($ temporaryPath );
117+
118+ $ dumper = new PhpDumper ($ container );
119+
120+ $ dumper ->setProxyDumper (new ProxyDumper ());
121+ $ containerFiles = $ dumper ->dump ([
122+ 'class ' => 'LazyServiceProjectAsFilesServiceContainer ' ,
123+ 'as_files ' => true ,
124+ ]);
125+
126+ array_walk (
127+ $ containerFiles ,
128+ static function (string $ containerSources , string $ fileName ) use ($ temporaryPath ): void {
129+ (new Filesystem ())->dumpFile ($ temporaryPath .'/ ' .$ fileName , $ containerSources );
130+ }
131+ );
132+
133+ require $ temporaryPath .'/LazyServiceProjectAsFilesServiceContainer.php ' ;
134+ }
42135}
43136
44137class TestManagerRegistry extends ManagerRegistry
0 commit comments