Skip to content

Commit bd5abf2

Browse files
[DependencyInjection] Fix dumping lazy services with parametrized class
1 parent 2db1a30 commit bd5abf2

File tree

5 files changed

+60
-33
lines changed

5 files changed

+60
-33
lines changed

ContainerBuilder.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -995,10 +995,14 @@ private function createService(Definition $definition, array &$inlineServices, b
995995
trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']);
996996
}
997997

998+
$parameterBag = $this->getParameterBag();
999+
9981000
if (true === $tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator ??= new LazyServiceInstantiator()) || $proxy instanceof RealServiceInstantiator) {
9991001
$proxy = $proxy->instantiateProxy(
10001002
$this,
1001-
$definition,
1003+
(clone $definition)
1004+
->setClass($parameterBag->resolveValue($definition->getClass()))
1005+
->setTags($parameterBag->resolveValue($definition->getTags())),
10021006
$id, function ($proxy = false) use ($definition, &$inlineServices, $id) {
10031007
return $this->createService($definition, $inlineServices, true, $id, $proxy);
10041008
}
@@ -1008,8 +1012,6 @@ private function createService(Definition $definition, array &$inlineServices, b
10081012
return $proxy;
10091013
}
10101014

1011-
$parameterBag = $this->getParameterBag();
1012-
10131015
if (null !== $definition->getFile()) {
10141016
require_once $parameterBag->resolveValue($definition->getFile());
10151017
}
@@ -1039,7 +1041,7 @@ private function createService(Definition $definition, array &$inlineServices, b
10391041
$service = $factory(...$arguments);
10401042

10411043
if (\is_object($tryProxy)) {
1042-
if (\get_class($service) !== $definition->getClass()) {
1044+
if (\get_class($service) !== $parameterBag->resolveValue($definition->getClass())) {
10431045
throw new LogicException(sprintf('Lazy service of type "%s" cannot be hydrated because its factory returned an unexpected instance of "%s". Try adding the "proxy" tag to the corresponding service definition with attribute "interface" set to "%1$s".', $definition->getClass(), get_debug_type($service)));
10441046
}
10451047

Dumper/PhpDumper.php

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ private function generateProxyClasses(): array
554554
$proxyDumper = $this->getProxyDumper();
555555
ksort($definitions);
556556
foreach ($definitions as $definition) {
557-
if (!$proxyDumper->isProxyCandidate($definition)) {
557+
if (!$definition = $this->isProxyCandidate($definition)) {
558558
continue;
559559
}
560560
if (isset($alreadyGenerated[$class = $definition->getClass()])) {
@@ -599,11 +599,11 @@ private function generateProxyClasses(): array
599599
return $proxyClasses;
600600
}
601601

602-
private function addServiceInclude(string $cId, Definition $definition): string
602+
private function addServiceInclude(string $cId, Definition $definition, bool $isProxyCandidate): string
603603
{
604604
$code = '';
605605

606-
if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) {
606+
if ($this->inlineRequires && (!$this->isHotPath($definition) || $isProxyCandidate)) {
607607
$lineage = [];
608608
foreach ($this->inlinedDefinitions as $def) {
609609
if (!$def->isDeprecated()) {
@@ -658,7 +658,7 @@ private function addServiceInstance(string $id, Definition $definition, bool $is
658658
}
659659

660660
$asGhostObject = false;
661-
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition, $asGhostObject);
661+
$isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject);
662662
$instantiation = '';
663663

664664
$lastWitherIndex = null;
@@ -886,7 +886,9 @@ protected function {$methodName}($lazyInitialization)
886886
}
887887

888888
$asGhostObject = false;
889-
if ($isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition, $asGhostObject)) {
889+
if ($isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject)) {
890+
$definition = $isProxyCandidate;
891+
890892
if (!$definition->isShared()) {
891893
$code .= sprintf(' %s ??= ', $factory);
892894

@@ -905,7 +907,7 @@ protected function {$methodName}($lazyInitialization)
905907
$code .= $asFile ? preg_replace('/function \(([^)]*+)\)( {|:)/', 'function (\1) use ($container)\2', $factoryCode) : $factoryCode;
906908
}
907909

908-
$c = $this->addServiceInclude($id, $definition);
910+
$c = $this->addServiceInclude($id, $definition, null !== $isProxyCandidate);
909911

910912
if ('' !== $c && $isProxyCandidate && !$definition->isShared()) {
911913
$c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c)));
@@ -1044,7 +1046,7 @@ private function addInlineService(string $id, Definition $definition, Definition
10441046
}
10451047

10461048
$asGhostObject = false;
1047-
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($inlineDef, $asGhostObject);
1049+
$isProxyCandidate = $this->isProxyCandidate($inlineDef, $asGhostObject);
10481050

10491051
if (isset($this->definitionVariables[$inlineDef])) {
10501052
$isSimpleInstance = false;
@@ -1307,9 +1309,8 @@ protected function load($file, $lazyLoad = true)
13071309
EOF;
13081310
}
13091311

1310-
$proxyDumper = $this->getProxyDumper();
13111312
foreach ($this->container->getDefinitions() as $definition) {
1312-
if (!$proxyDumper->isProxyCandidate($definition)) {
1313+
if (!$definition->isLazy() || $this->getProxyDumper() instanceof NullDumper) {
13131314
continue;
13141315
}
13151316

@@ -1496,7 +1497,7 @@ private function addInlineRequires(bool $hasProxyClasses): string
14961497
foreach ($hotPathServices as $id => $tags) {
14971498
$definition = $this->container->getDefinition($id);
14981499

1499-
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
1500+
if ($definition->isLazy() && !$this->getProxyDumper() instanceof NullDumper) {
15001501
continue;
15011502
}
15021503

@@ -1880,7 +1881,7 @@ private function dumpValue(mixed $value, bool $interpolate = true): string
18801881
}
18811882

18821883
$asGhostObject = false;
1883-
$this->getProxyDumper()->isProxyCandidate($value, $asGhostObject);
1884+
$this->isProxyCandidate($value, $asGhostObject);
18841885

18851886
return $this->addNewInstance($value, '', null, $asGhostObject);
18861887
} elseif ($value instanceof Variable) {
@@ -2252,6 +2253,7 @@ private function getAutoloadFile(): ?string
22522253
private function getClasses(Definition $definition, string $id): array
22532254
{
22542255
$classes = [];
2256+
$resolve = $this->container->getParameterBag()->resolveValue(...);
22552257

22562258
while ($definition instanceof Definition) {
22572259
foreach ($definition->getTag($this->preloadTags[0]) as $tag) {
@@ -2263,7 +2265,7 @@ private function getClasses(Definition $definition, string $id): array
22632265
}
22642266

22652267
if ($class = $definition->getClass()) {
2266-
$classes[] = trim($class, '\\');
2268+
$classes[] = trim($resolve($class), '\\');
22672269
}
22682270
$factory = $definition->getFactory();
22692271

@@ -2272,6 +2274,8 @@ private function getClasses(Definition $definition, string $id): array
22722274
}
22732275

22742276
if (\is_string($factory[0])) {
2277+
$factory[0] = $resolve($factory[0]);
2278+
22752279
if (false !== $i = strrpos($factory[0], '::')) {
22762280
$factory[0] = substr($factory[0], 0, $i);
22772281
}
@@ -2283,4 +2287,20 @@ private function getClasses(Definition $definition, string $id): array
22832287

22842288
return $classes;
22852289
}
2290+
2291+
private function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): ?Definition
2292+
{
2293+
$asGhostObject = false;
2294+
2295+
if (!$definition->isLazy() || ($proxyDumper = $this->getProxyDumper()) instanceof NullDumper) {
2296+
return null;
2297+
}
2298+
2299+
$bag = $this->container->getParameterBag();
2300+
$definition = (clone $definition)
2301+
->setClass($bag->resolveValue($definition->getClass()))
2302+
->setTags($bag->resolveValue($definition->getTags()));
2303+
2304+
return $proxyDumper->isProxyCandidate($definition, $asGhostObject) ? $definition : null;
2305+
}
22862306
}

Tests/ContainerBuilderTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,19 @@ public function testCreateProxyWithRealServiceInstantiator()
387387
$this->assertSame('Bar\FooClass', \get_class($foo1));
388388
}
389389

390+
public function testCreateLazyProxy()
391+
{
392+
$builder = new ContainerBuilder();
393+
394+
$builder->setParameter('foo1_class', 'Bar\FooClass');
395+
$builder->register('foo1', '%foo1_class%')->setLazy(true);
396+
397+
$foo1 = $builder->get('foo1');
398+
399+
$this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls');
400+
$this->assertInstanceOf(\Bar\FooClass::class, $foo1);
401+
}
402+
390403
public function testCreateServiceClass()
391404
{
392405
$builder = new ContainerBuilder();

Tests/Dumper/PhpDumperTest.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Psr\Container\ContainerInterface;
1616
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
17-
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
1817
use Symfony\Component\Config\FileLocator;
1918
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
2019
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
@@ -277,24 +276,22 @@ public function testDumpAsFilesWithFactoriesInlined()
277276
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_inlined_factories.txt', $dump);
278277
}
279278

280-
/**
281-
* @requires function \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper::getProxyCode
282-
*/
283279
public function testDumpAsFilesWithLazyFactoriesInlined()
284280
{
285281
$container = new ContainerBuilder();
286282
$container->setParameter('container.dumper.inline_factories', true);
287283
$container->setParameter('container.dumper.inline_class_loader', true);
284+
$container->setParameter('lazy_foo_class', \Bar\FooClass::class);
288285

289-
$container->register('lazy_foo', \Bar\FooClass::class)
286+
$container->register('lazy_foo', '%lazy_foo_class%')
290287
->addArgument(new Definition(\Bar\FooLazyClass::class))
291288
->setPublic(true)
292289
->setLazy(true);
293290

291+
$container->getCompilerPassConfig()->setOptimizationPasses([]);
294292
$container->compile();
295293

296294
$dumper = new PhpDumper($container);
297-
$dumper->setProxyDumper(new ProxyDumper());
298295
$dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot', 'build_time' => 1563381341]), true);
299296

300297
if ('\\' === \DIRECTORY_SEPARATOR) {

Tests/Fixtures/php/services9_lazy_inlined_factories.txt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Container%s;
66

77
include_once $this->targetDir.''.'/Fixtures/includes/foo.php';
88

9-
class FooClass_%s extends \Bar\FooClass implements \ProxyManager\Proxy\VirtualProxyInterface
9+
class FooClass_2b16075 extends \Bar\FooClass implements \Symfony\Component\VarExporter\LazyGhostObjectInterface
1010
%A
1111

1212
if (!\class_exists('FooClass_%s', false)) {
@@ -82,25 +82,19 @@ class ProjectServiceContainer extends Container
8282
/**
8383
* Gets the public 'lazy_foo' shared service.
8484
*
85-
* @return \Bar\FooClass
85+
* @return object A %lazy_foo_class% instance
8686
*/
8787
protected function getLazyFooService($lazyLoad = true)
8888
{
8989
if (true === $lazyLoad) {
90-
return $this->services['lazy_foo'] = $this->createProxy('FooClass_8976cfa', function () {
91-
return \FooClass_8976cfa::staticProxyConstructor(function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {
92-
$wrappedInstance = $this->getLazyFooService(false);
93-
94-
$proxy->setProxyInitializer(null);
95-
96-
return true;
97-
});
90+
return $this->services['lazy_foo'] = $this->createProxy('FooClass_2b16075', function () {
91+
return \FooClass_2b16075::createLazyGhostObject($this->getLazyFooService(...));
9892
});
9993
}
10094

10195
include_once $this->targetDir.''.'/Fixtures/includes/foo_lazy.php';
10296

103-
return new \Bar\FooClass(new \Bar\FooLazyClass());
97+
return ($lazyLoad->__construct(new \Bar\FooLazyClass()) && false ?: $lazyLoad);
10498
}
10599

106100
public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
@@ -162,6 +156,7 @@ class ProjectServiceContainer extends Container
162156
return [
163157
'container.dumper.inline_factories' => true,
164158
'container.dumper.inline_class_loader' => true,
159+
'lazy_foo_class' => 'Bar\\FooClass',
165160
];
166161
}
167162
}

0 commit comments

Comments
 (0)