Skip to content

Commit 9ea5995

Browse files
committed
[DependencyInjection] Fix cloned proxies not sharing their dependencies
1 parent bce5a9b commit 9ea5995

File tree

7 files changed

+55
-11
lines changed

7 files changed

+55
-11
lines changed

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,9 +2186,9 @@ private function isSingleUsePrivateNode(ServiceReferenceGraphNode $node): bool
21862186
return false;
21872187
}
21882188

2189-
// When the source node is a ghost, it will use its references only when constructed.
2190-
// Since a ghost can be cloned, we do not know how often its references are constructed.
2191-
if ($this->getProxyDumper()->isProxyCandidate($value, $asGhostObject) && $asGhostObject) {
2189+
// When the source node is a proxy or ghost, it will construct its references only when the node itself is initialized.
2190+
// Since the node can be cloned before being fully initialized, we do not know how often its references are used.
2191+
if ($this->getProxyDumper()->isProxyCandidate($value)) {
21922192
return false;
21932193
}
21942194
$ids[$edge->getSourceNode()->getId()] = true;

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
use Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation;
5757
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
5858
use Symfony\Component\DependencyInjection\Tests\Fixtures\DependencyContainer;
59+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DependencyContainerInterface;
5960
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute;
6061
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum;
6162
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
@@ -1678,7 +1679,7 @@ public function testWitherWithStaticReturnType()
16781679
$this->assertInstanceOf(Foo::class, $wither->foo);
16791680
}
16801681

1681-
public function testCloningLazyWithDependency()
1682+
public function testCloningLazyGhostWithDependency()
16821683
{
16831684
$container = new ContainerBuilder();
16841685
$container->register('dependency', \stdClass::class);
@@ -1689,10 +1690,10 @@ public function testCloningLazyWithDependency()
16891690

16901691
$container->compile();
16911692
$dumper = new PhpDumper($container);
1692-
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_CloningLazyWithDependency']);
1693+
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_CloningLazyGhostWithDependency']);
16931694
eval('?>'.$dump);
16941695

1695-
$container = new \Symfony_DI_PhpDumper_Service_CloningLazyWithDependency();
1696+
$container = new \Symfony_DI_PhpDumper_Service_CloningLazyGhostWithDependency();
16961697

16971698
$bar = $container->get(DependencyContainer::class);
16981699
$this->assertInstanceOf(DependencyContainer::class, $bar);
@@ -1703,6 +1704,34 @@ public function testCloningLazyWithDependency()
17031704
$this->assertSame($first_clone->dependency, $second_clone->dependency);
17041705
}
17051706

1707+
public function testCloningProxyWithDependency()
1708+
{
1709+
$container = new ContainerBuilder();
1710+
$container->register('dependency', \stdClass::class);
1711+
$container->register(DependencyContainer::class)
1712+
->addArgument(new Reference('dependency'))
1713+
->setLazy(true)
1714+
->addTag('proxy', [
1715+
'interface' => DependencyContainerInterface::class,
1716+
])
1717+
->setPublic(true);
1718+
1719+
$container->compile();
1720+
$dumper = new PhpDumper($container);
1721+
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_CloningProxyWithDependency']);
1722+
eval('?>'.$dump);
1723+
1724+
$container = new \Symfony_DI_PhpDumper_Service_CloningProxyWithDependency();
1725+
1726+
$bar = $container->get(DependencyContainer::class);
1727+
$this->assertInstanceOf(DependencyContainerInterface::class, $bar);
1728+
1729+
$first_clone = clone $bar;
1730+
$second_clone = clone $bar;
1731+
1732+
$this->assertSame($first_clone->getDependency(), $second_clone->getDependency());
1733+
}
1734+
17061735
public function testCurrentFactoryInlining()
17071736
{
17081737
$container = new ContainerBuilder();

src/Symfony/Component/DependencyInjection/Tests/Fixtures/DependencyContainer.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
44

5-
class DependencyContainer
5+
class DependencyContainer implements DependencyContainerInterface
66
{
77

88
public function __construct(
99
public mixed $dependency,
1010
)
1111
{
1212
}
13+
14+
public function getDependency(): mixed
15+
{
16+
return $this->dependency;
17+
}
18+
1319
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
interface DependencyContainerInterface
6+
{
7+
8+
public function getDependency(): mixed;
9+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/closure_proxy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@ protected function createProxy($class, \Closure $factory)
5555
*/
5656
protected static function getClosureProxyService($container, $lazyLoad = true)
5757
{
58-
return $container->services['closure_proxy'] = new class(fn () => (new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure implements \Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface { public function theMethod() { return $this->service->cloneFoo(...\func_get_args()); } };
58+
return $container->services['closure_proxy'] = new class(fn () => ($container->privates['foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure implements \Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface { public function theMethod() { return $this->service->cloneFoo(...\func_get_args()); } };
5959
}
6060
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_closure.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ protected function createProxy($class, \Closure $factory)
5757
*/
5858
protected static function getClosure1Service($container, $lazyLoad = true)
5959
{
60-
return $container->services['closure1'] = (new class(fn () => (new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function cloneFoo(?\stdClass $bar = null): \Symfony\Component\DependencyInjection\Tests\Compiler\Foo { return $this->service->cloneFoo(...\func_get_args()); } })->cloneFoo(...);
60+
return $container->services['closure1'] = (new class(fn () => ($container->privates['foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function cloneFoo(?\stdClass $bar = null): \Symfony\Component\DependencyInjection\Tests\Compiler\Foo { return $this->service->cloneFoo(...\func_get_args()); } })->cloneFoo(...);
6161
}
6262

6363
/**
@@ -67,6 +67,6 @@ protected static function getClosure1Service($container, $lazyLoad = true)
6767
*/
6868
protected static function getClosure2Service($container, $lazyLoad = true)
6969
{
70-
return $container->services['closure2'] = (new class(fn () => (new \Symfony\Component\DependencyInjection\Tests\Compiler\FooVoid())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function __invoke(string $name): void { $this->service->__invoke(...\func_get_args()); } })->__invoke(...);
70+
return $container->services['closure2'] = (new class(fn () => ($container->privates['foo_void'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\FooVoid())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function __invoke(string $name): void { $this->service->__invoke(...\func_get_args()); } })->__invoke(...);
7171
}
7272
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ protected static function getWitherService($container, $lazyLoad = true)
6161

6262
$instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither();
6363

64-
$a = (new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo());
64+
$a = ($container->privates['Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo());
6565

6666
$instance = $instance->withFoo1($a);
6767
$instance = $instance->withFoo2($a);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy