Skip to content

Commit ef2626f

Browse files
committed
[DependencyInjection] Fix fetching lazy non-shared services multiple times
1 parent 0be2622 commit ef2626f

File tree

4 files changed

+237
-1
lines changed

4 files changed

+237
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,8 @@ protected function {$methodName}($lazyInitialization)
900900

901901
if ($asFile) {
902902
$code .= "fn () => self::do(\$container);\n\n";
903+
} elseif ($definition->isPublic()) {
904+
$code .= sprintf("fn () => \$this->%s();\n\n", $methodName);
903905
} else {
904906
$code .= sprintf("\$this->%s(...);\n\n", $methodName);
905907
}

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

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,42 @@ public function testInlinedDefinitionReferencingServiceContainer()
692692
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
693693
}
694694

695+
public function testNonSharedLazy()
696+
{
697+
$container = new ContainerBuilder();
698+
699+
$container
700+
->register('foo', Foo::class)
701+
->setFile(realpath(self::$fixturesPath.'/includes/foo_lazy.php'))
702+
->setShared(false)
703+
->setLazy(true)
704+
->setPublic(true);
705+
706+
$container->compile();
707+
$dumper = new PhpDumper($container);
708+
$dump = $dumper->dump([
709+
'class' => 'Symfony_DI_PhpDumper_Service_Non_Shared_Lazy',
710+
'file' => __DIR__,
711+
'inline_factories_parameter' => false,
712+
'inline_class_loader_parameter' => false,
713+
]);
714+
$this->assertStringEqualsFile(
715+
self::$fixturesPath.'/php/services_non_shared_lazy_public.php',
716+
'\\' === \DIRECTORY_SEPARATOR ? str_replace("'.\\DIRECTORY_SEPARATOR.'", '/', $dump) : $dump
717+
);
718+
eval('?>'.$dump);
719+
720+
$container = new \Symfony_DI_PhpDumper_Service_Non_Shared_Lazy();
721+
722+
$foo1 = $container->get('foo');
723+
$this->assertTrue($foo1->resetLazyObject());
724+
725+
$foo2 = $container->get('foo');
726+
$this->assertTrue($foo2->resetLazyObject());
727+
728+
$this->assertNotSame($foo1, $foo2);
729+
}
730+
695731
/**
696732
* @testWith [false]
697733
* [true]
@@ -700,7 +736,7 @@ public function testNonSharedLazyDefinitionReferences(bool $asGhostObject)
700736
{
701737
$container = new ContainerBuilder();
702738
$container->register('foo', 'stdClass')->setShared(false)->setLazy(true);
703-
$container->register('bar', 'stdClass')->addArgument(new Reference('foo', ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, false))->setPublic(true);
739+
$container->register('bar', 'stdClass')->addArgument(new Reference('foo', ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE))->setPublic(true);
704740
$container->compile();
705741

706742
$dumper = new PhpDumper($container);
@@ -1459,6 +1495,37 @@ public function testLazyWither()
14591495
$this->assertTrue($wither->resetLazyObject());
14601496
}
14611497

1498+
public function testLazyWitherNonShared()
1499+
{
1500+
$container = new ContainerBuilder();
1501+
$container->register(Foo::class);
1502+
1503+
$container
1504+
->register('wither', Wither::class)
1505+
->setShared(false)
1506+
->setLazy(true)
1507+
->setPublic(true)
1508+
->setAutowired(true);
1509+
1510+
$container->compile();
1511+
$dumper = new PhpDumper($container);
1512+
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_Wither_Lazy_Non_Shared']);
1513+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_wither_lazy_non_shared.php', $dump);
1514+
eval('?>'.$dump);
1515+
1516+
$container = new \Symfony_DI_PhpDumper_Service_Wither_Lazy_Non_Shared();
1517+
1518+
$wither1 = $container->get('wither');
1519+
$this->assertInstanceOf(Foo::class, $wither1->foo);
1520+
$this->assertTrue($wither1->resetLazyObject());
1521+
1522+
$wither2 = $container->get('wither');
1523+
$this->assertInstanceOf(Foo::class, $wither2->foo);
1524+
$this->assertTrue($wither2->resetLazyObject());
1525+
1526+
$this->assertNotSame($wither1, $wither2);
1527+
}
1528+
14621529
public function testWitherWithStaticReturnType()
14631530
{
14641531
$container = new ContainerBuilder();
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\LogicException;
7+
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11+
12+
/**
13+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
14+
*/
15+
class Symfony_DI_PhpDumper_Service_Non_Shared_Lazy extends Container
16+
{
17+
protected $parameters = [];
18+
19+
public function __construct()
20+
{
21+
$this->services = $this->privates = [];
22+
$this->methodMap = [
23+
'foo' => 'getFooService',
24+
];
25+
26+
$this->aliases = [];
27+
}
28+
29+
public function compile(): void
30+
{
31+
throw new LogicException('You cannot compile a dumped container that was already compiled.');
32+
}
33+
34+
public function isCompiled(): bool
35+
{
36+
return true;
37+
}
38+
39+
protected function createProxy($class, \Closure $factory)
40+
{
41+
return $factory();
42+
}
43+
44+
/**
45+
* Gets the public 'foo' service.
46+
*
47+
* @return \Symfony\Component\DependencyInjection\Tests\Compiler\Foo
48+
*/
49+
protected function getFooService($lazyLoad = true)
50+
{
51+
$this->factories['foo'] ??= fn () => $this->getFooService();
52+
53+
if (true === $lazyLoad) {
54+
return $this->createProxy('FooGhostCf082ac', fn () => \FooGhostCf082ac::createLazyGhost($this->getFooService(...)));
55+
}
56+
57+
static $include = true;
58+
59+
if ($include) {
60+
include_once __DIR__.'/Fixtures/includes/foo_lazy.php';
61+
62+
$include = false;
63+
}
64+
65+
return $lazyLoad;
66+
}
67+
}
68+
69+
class FooGhostCf082ac extends \Symfony\Component\DependencyInjection\Tests\Compiler\Foo implements \Symfony\Component\VarExporter\LazyObjectInterface
70+
{
71+
use \Symfony\Component\VarExporter\LazyGhostTrait;
72+
73+
private const LAZY_OBJECT_PROPERTY_SCOPES = [];
74+
}
75+
76+
// Help opcache.preload discover always-needed symbols
77+
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
78+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
79+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\LogicException;
7+
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11+
12+
/**
13+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
14+
*/
15+
class Symfony_DI_PhpDumper_Service_Wither_Lazy_Non_Shared extends Container
16+
{
17+
protected $parameters = [];
18+
19+
public function __construct()
20+
{
21+
$this->services = $this->privates = [];
22+
$this->methodMap = [
23+
'wither' => 'getWitherService',
24+
];
25+
26+
$this->aliases = [];
27+
}
28+
29+
public function compile(): void
30+
{
31+
throw new LogicException('You cannot compile a dumped container that was already compiled.');
32+
}
33+
34+
public function isCompiled(): bool
35+
{
36+
return true;
37+
}
38+
39+
public function getRemovedIds(): array
40+
{
41+
return [
42+
'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true,
43+
];
44+
}
45+
46+
protected function createProxy($class, \Closure $factory)
47+
{
48+
return $factory();
49+
}
50+
51+
/**
52+
* Gets the public 'wither' autowired service.
53+
*
54+
* @return \Symfony\Component\DependencyInjection\Tests\Compiler\Wither
55+
*/
56+
protected function getWitherService($lazyLoad = true)
57+
{
58+
$this->factories['wither'] ??= fn () => $this->getWitherService();
59+
60+
if (true === $lazyLoad) {
61+
return $this->createProxy('WitherProxy8cb632f', fn () => \WitherProxy8cb632f::createLazyProxy(fn () => $this->getWitherService(false)));
62+
}
63+
64+
$instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither();
65+
66+
$a = ($this->privates['Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo());
67+
68+
$instance = $instance->withFoo1($a);
69+
$instance = $instance->withFoo2($a);
70+
$instance->setFoo($a);
71+
72+
return $instance;
73+
}
74+
}
75+
76+
class WitherProxy8cb632f extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface
77+
{
78+
use \Symfony\Component\VarExporter\LazyProxyTrait;
79+
80+
private const LAZY_OBJECT_PROPERTY_SCOPES = [
81+
'foo' => [parent::class, 'foo', null],
82+
];
83+
}
84+
85+
// Help opcache.preload discover always-needed symbols
86+
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
87+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
88+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);

0 commit comments

Comments
 (0)