Skip to content

Commit 6fe3a52

Browse files
[DependencyInjection] Fix dumping lazy-proxy of interfaces
1 parent 54f7ec5 commit 6fe3a52

File tree

8 files changed

+64
-32
lines changed

8 files changed

+64
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ CHANGELOG
55
---
66

77
* Use lazy-loading ghost objects and virtual proxies out of the box
8-
* Add argument `&$asGhostObject` to LazyProxy's `DumperInterface` to allow using ghost objects for lazy loading services
8+
* Add arguments `&$asGhostObject` and `$id` to LazyProxy's `DumperInterface` to allow using ghost objects for lazy loading services
99
* Add `enum` env var processor
1010
* Add `shuffle` env var processor
1111
* Add `resolve-env` option to `debug:config` command to display actual values of environment variables in dumped configuration

Dumper/PhpDumper.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,8 @@ private function generateProxyClasses(): array
550550
$strip = '' === $this->docStar && method_exists(Kernel::class, 'stripComments');
551551
$proxyDumper = $this->getProxyDumper();
552552
ksort($definitions);
553-
foreach ($definitions as $definition) {
554-
if (!$definition = $this->isProxyCandidate($definition)) {
553+
foreach ($definitions as $id => $definition) {
554+
if (!$definition = $this->isProxyCandidate($definition, $asGhostObject, $id)) {
555555
continue;
556556
}
557557
if (isset($alreadyGenerated[$class = $definition->getClass()])) {
@@ -560,7 +560,7 @@ private function generateProxyClasses(): array
560560
$alreadyGenerated[$class] = true;
561561
// register class' reflector for resource tracking
562562
$this->container->getReflectionClass($class);
563-
if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) {
563+
if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition, $id)) {
564564
continue;
565565
}
566566

@@ -655,7 +655,7 @@ private function addServiceInstance(string $id, Definition $definition, bool $is
655655
}
656656

657657
$asGhostObject = false;
658-
$isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject);
658+
$isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject, $id);
659659
$instantiation = '';
660660

661661
$lastWitherIndex = null;
@@ -883,7 +883,7 @@ protected function {$methodName}($lazyInitialization)
883883
}
884884

885885
$asGhostObject = false;
886-
if ($isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject)) {
886+
if ($isProxyCandidate = $this->isProxyCandidate($definition, $asGhostObject, $id)) {
887887
$definition = $isProxyCandidate;
888888

889889
if (!$definition->isShared()) {
@@ -1041,7 +1041,7 @@ private function addInlineService(string $id, Definition $definition, Definition
10411041
}
10421042

10431043
$asGhostObject = false;
1044-
$isProxyCandidate = $this->isProxyCandidate($inlineDef, $asGhostObject);
1044+
$isProxyCandidate = $this->isProxyCandidate($inlineDef, $asGhostObject, $id);
10451045

10461046
if (isset($this->definitionVariables[$inlineDef])) {
10471047
$isSimpleInstance = false;
@@ -2266,7 +2266,7 @@ private function getClasses(Definition $definition, string $id): array
22662266
return $classes;
22672267
}
22682268

2269-
private function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): ?Definition
2269+
private function isProxyCandidate(Definition $definition, ?bool &$asGhostObject, string $id): ?Definition
22702270
{
22712271
$asGhostObject = false;
22722272

@@ -2279,6 +2279,6 @@ private function isProxyCandidate(Definition $definition, bool &$asGhostObject =
22792279
->setClass($bag->resolveValue($definition->getClass()))
22802280
->setTags(($definition->hasTag('proxy') ? ['proxy' => $bag->resolveValue($definition->getTag('proxy'))] : []) + $definition->getTags());
22812281

2282-
return $proxyDumper->isProxyCandidate($definition, $asGhostObject) ? $definition : null;
2282+
return $proxyDumper->isProxyCandidate($definition, $asGhostObject, $id) ? $definition : null;
22832283
}
22842284
}

LazyProxy/Instantiator/LazyServiceInstantiator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function instantiateProxy(ContainerInterface $container, Definition $defi
2626
$dumper = new LazyServiceDumper();
2727

2828
if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $class), false)) {
29-
eval($dumper->getProxyCode($definition));
29+
eval($dumper->getProxyCode($definition, $id));
3030
}
3131

3232
return isset(class_uses($proxyClass)[LazyGhostTrait::class]) ? $proxyClass::createLazyGhost($realInstantiator) : $proxyClass::createLazyProxy($realInstantiator);

LazyProxy/PhpDumper/DumperInterface.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ interface DumperInterface
2323
/**
2424
* Inspects whether the given definitions should produce proxy instantiation logic in the dumped container.
2525
*
26-
* @param bool|null &$asGhostObject Set to true after the call if the proxy is a ghost object
26+
* @param bool|null &$asGhostObject Set to true after the call if the proxy is a ghost object
27+
* @param string|null $id
2728
*/
28-
public function isProxyCandidate(Definition $definition/* , bool &$asGhostObject = null */): bool;
29+
public function isProxyCandidate(Definition $definition/* , bool &$asGhostObject = null, string $id = null */): bool;
2930

3031
/**
3132
* Generates the code to be used to instantiate a proxy in the dumped factory code.
@@ -35,5 +36,5 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $
3536
/**
3637
* Generates the code for the lazy proxy.
3738
*/
38-
public function getProxyCode(Definition $definition): string;
39+
public function getProxyCode(Definition $definition/* , string $id = null */): string;
3940
}

LazyProxy/PhpDumper/LazyServiceDumper.php

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public function __construct(
2626
) {
2727
}
2828

29-
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool
29+
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null, string $id = null): bool
3030
{
3131
$asGhostObject = false;
3232

3333
if ($definition->hasTag('proxy')) {
3434
if (!$definition->isLazy()) {
35-
throw new InvalidArgumentException(sprintf('Invalid definition for service of class "%s": setting the "proxy" tag on a service requires it to be "lazy".', $definition->getClass()));
35+
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": setting the "proxy" tag on a service requires it to be "lazy".', $id ?? $definition->getClass()));
3636
}
3737

3838
return true;
@@ -99,43 +99,45 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $
9999
EOF;
100100
}
101101

102-
public function getProxyCode(Definition $definition): string
102+
public function getProxyCode(Definition $definition, string $id = null): string
103103
{
104-
if (!$this->isProxyCandidate($definition, $asGhostObject)) {
105-
throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service of class "%s".', $definition->getClass()));
104+
if (!$this->isProxyCandidate($definition, $asGhostObject, $id)) {
105+
throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service "%s".', $id ?? $definition->getClass()));
106106
}
107107
$proxyClass = $this->getProxyClass($definition, $class);
108108

109109
if ($asGhostObject) {
110110
try {
111111
return 'class '.$proxyClass.ProxyHelper::generateLazyGhost($class);
112112
} catch (LogicException $e) {
113-
throw new InvalidArgumentException(sprintf('Cannot generate lazy ghost for service of class "%s" lazy.', $definition->getClass()), 0, $e);
113+
throw new InvalidArgumentException(sprintf('Cannot generate lazy ghost for service "%s".', $id ?? $definition->getClass()), 0, $e);
114114
}
115115
}
116+
$interfaces = [];
116117

117118
if ($definition->hasTag('proxy')) {
118-
$interfaces = [];
119119
foreach ($definition->getTag('proxy') as $tag) {
120120
if (!isset($tag['interface'])) {
121-
throw new InvalidArgumentException(sprintf('Invalid definition for service of class "%s": the "interface" attribute is missing on a "proxy" tag.', $definition->getClass()));
121+
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": the "interface" attribute is missing on a "proxy" tag.', $id ?? $definition->getClass()));
122122
}
123123
if (!interface_exists($tag['interface']) && !class_exists($tag['interface'], false)) {
124-
throw new InvalidArgumentException(sprintf('Invalid definition for service of class "%s": several "proxy" tags found but "%s" is not an interface.', $definition->getClass(), $tag['interface']));
124+
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": several "proxy" tags found but "%s" is not an interface.', $id ?? $definition->getClass(), $tag['interface']));
125125
}
126126
$interfaces[] = new \ReflectionClass($tag['interface']);
127127
}
128-
} else {
128+
129+
if (1 === \count($interfaces) && !$interfaces[0]->isInterface()) {
130+
$class = array_pop($interfaces);
131+
}
132+
} elseif ($class->isInterface()) {
129133
$interfaces = [$class];
130-
}
131-
if (1 === \count($interfaces) && !$interfaces[0]->isInterface()) {
132-
$class = array_pop($interfaces);
134+
$class = null;
133135
}
134136

135137
try {
136-
return (\PHP_VERSION_ID >= 80200 && $class->isReadOnly() ? 'readonly ' : '').'class '.$proxyClass.ProxyHelper::generateLazyProxy($class, $interfaces);
138+
return (\PHP_VERSION_ID >= 80200 && $class?->isReadOnly() ? 'readonly ' : '').'class '.$proxyClass.ProxyHelper::generateLazyProxy($class, $interfaces);
137139
} catch (LogicException $e) {
138-
throw new InvalidArgumentException(sprintf('Cannot generate lazy proxy for service of class "%s" lazy.', $definition->getClass()), 0, $e);
140+
throw new InvalidArgumentException(sprintf('Cannot generate lazy proxy for service "%s".', $id ?? $definition->getClass()), 0, $e);
139141
}
140142
}
141143

LazyProxy/PhpDumper/NullDumper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*/
2323
class NullDumper implements DumperInterface
2424
{
25-
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool
25+
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null, string $id = null): bool
2626
{
2727
return $asGhostObject = false;
2828
}
@@ -32,7 +32,7 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $
3232
return '';
3333
}
3434

35-
public function getProxyCode(Definition $definition): string
35+
public function getProxyCode(Definition $definition, string $id = null): string
3636
{
3737
return '';
3838
}

Tests/Fixtures/includes/classes.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function callPassed()
8484

8585
class DummyProxyDumper implements ProxyDumper
8686
{
87-
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool
87+
public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null, string $id = null): bool
8888
{
8989
$asGhostObject = false;
9090

@@ -96,7 +96,7 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $
9696
return " // lazy factory for {$definition->getClass()}\n\n";
9797
}
9898

99-
public function getProxyCode(Definition $definition): string
99+
public function getProxyCode(Definition $definition, $id = null): string
100100
{
101101
return "// proxy code for {$definition->getClass()}\n";
102102
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\PhpDumper;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Psr\Container\ContainerInterface;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper;
18+
19+
class LazyServiceDumperTest extends TestCase
20+
{
21+
public function testProxyInterface()
22+
{
23+
$dumper = new LazyServiceDumper();
24+
$definition = (new Definition(ContainerInterface::class))->setLazy(true);
25+
26+
$this->assertTrue($dumper->isProxyCandidate($definition));
27+
$this->assertStringContainsString('function get(', $dumper->getProxyCode($definition));
28+
}
29+
}

0 commit comments

Comments
 (0)