Skip to content

Commit 20e1df3

Browse files
[DependencyInjection][VarExporter] Generate lazy proxies for non-ghostable lazy services out of the box
1 parent e788a49 commit 20e1df3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1821
-521
lines changed

.github/expected-missing-return-types.diff

+23-23
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff)
77
git checkout composer.json src/
88

99
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
10-
index 165797504b..0c0922088a 100644
10+
index 18b5c21b9f..8fca8244e3 100644
1111
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
1212
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
13-
@@ -87,5 +87,5 @@ abstract class KernelTestCase extends TestCase
13+
@@ -88,5 +88,5 @@ abstract class KernelTestCase extends TestCase
1414
* @return Container
1515
*/
1616
- protected static function getContainer(): ContainerInterface
@@ -156,52 +156,52 @@ index 6b1c6c5fbe..bb80ed461e 100644
156156
+ public function isFresh(ResourceInterface $resource, int $timestamp): bool;
157157
}
158158
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
159-
index 64068fcc23..f29aaf1b94 100644
159+
index 53f2021fa9..cf95c1fe99 100644
160160
--- a/src/Symfony/Component/Console/Application.php
161161
+++ b/src/Symfony/Component/Console/Application.php
162-
@@ -218,5 +218,5 @@ class Application implements ResetInterface
162+
@@ -219,5 +219,5 @@ class Application implements ResetInterface
163163
* @return int 0 if everything went fine, or an error code
164164
*/
165165
- public function doRun(InputInterface $input, OutputInterface $output)
166166
+ public function doRun(InputInterface $input, OutputInterface $output): int
167167
{
168168
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
169-
@@ -454,5 +454,5 @@ class Application implements ResetInterface
169+
@@ -453,5 +453,5 @@ class Application implements ResetInterface
170170
* @return string
171171
*/
172172
- public function getLongVersion()
173173
+ public function getLongVersion(): string
174174
{
175175
if ('UNKNOWN' !== $this->getName()) {
176-
@@ -497,5 +497,5 @@ class Application implements ResetInterface
176+
@@ -496,5 +496,5 @@ class Application implements ResetInterface
177177
* @return Command|null
178178
*/
179179
- public function add(Command $command)
180180
+ public function add(Command $command): ?Command
181181
{
182182
$this->init();
183-
@@ -534,5 +534,5 @@ class Application implements ResetInterface
183+
@@ -533,5 +533,5 @@ class Application implements ResetInterface
184184
* @throws CommandNotFoundException When given command name does not exist
185185
*/
186186
- public function get(string $name)
187187
+ public function get(string $name): Command
188188
{
189189
$this->init();
190-
@@ -641,5 +641,5 @@ class Application implements ResetInterface
190+
@@ -640,5 +640,5 @@ class Application implements ResetInterface
191191
* @throws CommandNotFoundException When command name is incorrect or ambiguous
192192
*/
193193
- public function find(string $name)
194194
+ public function find(string $name): Command
195195
{
196196
$this->init();
197-
@@ -751,5 +751,5 @@ class Application implements ResetInterface
197+
@@ -750,5 +750,5 @@ class Application implements ResetInterface
198198
* @return Command[]
199199
*/
200200
- public function all(string $namespace = null)
201201
+ public function all(string $namespace = null): array
202202
{
203203
$this->init();
204-
@@ -950,5 +950,5 @@ class Application implements ResetInterface
204+
@@ -949,5 +949,5 @@ class Application implements ResetInterface
205205
* @return int 0 if everything went fine, or an error code
206206
*/
207207
- protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
@@ -234,7 +234,7 @@ index b41e691537..34de10fa70 100644
234234
{
235235
if (null === $this->helperSet) {
236236
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
237-
index 3c6b0efccd..121664f15a 100644
237+
index 0112350a50..dc972564fb 100644
238238
--- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php
239239
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
240240
@@ -137,5 +137,5 @@ class OutputFormatter implements WrappableOutputFormatterInterface
@@ -301,22 +301,22 @@ index 2f1631ed30..a4b572771e 100644
301301
{
302302
if (\is_array($value)) {
303303
diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
304-
index 04b7022484..5d736ec754 100644
304+
index 20ca68e514..e0850df0f8 100644
305305
--- a/src/Symfony/Component/DependencyInjection/Container.php
306306
+++ b/src/Symfony/Component/DependencyInjection/Container.php
307-
@@ -108,5 +108,5 @@ class Container implements ContainerInterface, ResetInterface
308-
* @throws InvalidArgumentException if the parameter is not defined
307+
@@ -109,5 +109,5 @@ class Container implements ContainerInterface, ResetInterface
308+
* @throws ParameterNotFoundException if the parameter is not defined
309309
*/
310310
- public function getParameter(string $name)
311311
+ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
312312
{
313313
return $this->parameterBag->get($name);
314314
diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
315-
index cad44026c0..14cd192e07 100644
315+
index 9e97fb71fc..1cda97c611 100644
316316
--- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php
317317
+++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
318318
@@ -53,5 +53,5 @@ interface ContainerInterface extends PsrContainerInterface
319-
* @throws InvalidArgumentException if the parameter is not defined
319+
* @throws ParameterNotFoundException if the parameter is not defined
320320
*/
321321
- public function getParameter(string $name);
322322
+ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null;
@@ -358,7 +358,7 @@ index d553203c43..1163f4b107 100644
358358
{
359359
$class = static::class;
360360
diff --git a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
361-
index 4f66f18073..e96d867296 100644
361+
index 11cda00cc5..07b4990160 100644
362362
--- a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
363363
+++ b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
364364
@@ -35,5 +35,5 @@ interface ExtensionInterface
@@ -913,7 +913,7 @@ index 5014b9bd51..757c76f546 100644
913913
+ public function supportsDecoding(string $format): bool;
914914
}
915915
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
916-
index 391cdcb39c..f637687e74 100644
916+
index e426d87076..350cbc6335 100644
917917
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
918918
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
919919
@@ -213,5 +213,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
@@ -1094,11 +1094,11 @@ index b22d6ae609..31d1a25f9d 100644
10941094
+ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic): array
10951095
{
10961096
$refs = $values;
1097-
diff --git a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
1098-
index 471c1a6b91..2e19d2ab2d 100644
1099-
--- a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
1100-
+++ b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
1101-
@@ -54,5 +54,5 @@ class GhostObjectState
1097+
diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
1098+
index 6ea89bc831..01748bcfc0 100644
1099+
--- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
1100+
+++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
1101+
@@ -56,5 +56,5 @@ class LazyObjectState
11021102
* @return bool Returns true when fully-initializing, false when partial-initializing
11031103
*/
11041104
- public function initialize($instance, $propertyName, $propertyScope)

src/Symfony/Bridge/Doctrine/ManagerRegistry.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use ProxyManager\Proxy\GhostObjectInterface;
1616
use ProxyManager\Proxy\LazyLoadingInterface;
1717
use Symfony\Component\DependencyInjection\Container;
18-
use Symfony\Component\VarExporter\LazyGhostObjectInterface;
18+
use Symfony\Component\VarExporter\LazyObjectInterface;
1919

2020
/**
2121
* References Doctrine connections and entity/document managers.
@@ -41,8 +41,8 @@ protected function resetService($name): void
4141
}
4242
$manager = $this->container->get($name);
4343

44-
if ($manager instanceof LazyGhostObjectInterface) {
45-
if (!$manager->resetLazyGhostObject()) {
44+
if ($manager instanceof LazyObjectInterface) {
45+
if (!$manager->resetLazyObject()) {
4646
throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Declare the "%s" service as lazy.', $name));
4747
}
4848

src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPUnit\Framework\TestCase;
1717
use ProxyManager\Proxy\LazyLoadingInterface;
1818
use ProxyManagerBridgeFooClass;
19+
use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
1920
use Symfony\Component\DependencyInjection\ContainerBuilder;
2021

2122
/**
@@ -29,6 +30,7 @@ class ContainerBuilderTest extends TestCase
2930
public function testCreateProxyServiceWithRuntimeInstantiator()
3031
{
3132
$builder = new ContainerBuilder();
33+
$builder->setProxyInstantiator(new RuntimeInstantiator());
3234

3335
$builder->register('foo1', ProxyManagerBridgeFooClass::class)->setFile(__DIR__.'/Fixtures/includes/foo.php')->setPublic(true);
3436
$builder->getDefinition('foo1')->setLazy(true)->addTag('proxy', ['interface' => ProxyManagerBridgeFooClass::class]);

src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use ProxyManager\Proxy\LazyLoadingInterface;
16+
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
1819

@@ -66,6 +67,7 @@ private function dumpLazyServiceProjectServiceContainer()
6667
$container->compile();
6768

6869
$dumper = new PhpDumper($container);
70+
$dumper->setProxyDumper(new ProxyDumper());
6971

7072
return $dumper->dump(['class' => 'LazyServiceProjectServiceContainer']);
7173
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CHANGELOG
44
6.2
55
---
66

7-
* Use lazy-loading ghost object proxies out of the box
7+
* Use lazy-loading ghost objects and virtual proxies out of the box
88
* Add argument `&$asGhostObject` 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

src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
use Symfony\Component\DependencyInjection\Definition;
2525
use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
2626
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
27-
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
2827
use Symfony\Component\DependencyInjection\Reference;
2928
use Symfony\Component\DependencyInjection\TypedReference;
29+
use Symfony\Component\VarExporter\ProxyHelper;
3030
use Symfony\Contracts\Service\Attribute\SubscribedService;
3131

3232
/**
@@ -276,7 +276,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
276276
continue;
277277
}
278278

279-
$type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
279+
$type = ProxyHelper::exportType($parameter, true);
280280

281281
if ($checkAttributes) {
282282
foreach ($parameter->getAttributes() as $attribute) {
@@ -306,8 +306,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
306306
--$index;
307307
break;
308308
}
309-
$type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
310-
$type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint';
309+
$type = ProxyHelper::exportType($parameter);
310+
$type = $type ? sprintf('is type-hinted "%s"', preg_replace('/(^|[(|&])\\\\|^\?\\\\?/', '\1', $type)) : 'has no type-hint';
311311

312312
throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type));
313313
}

src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
use Symfony\Component\DependencyInjection\Definition;
2020
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2121
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
22-
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
2322
use Symfony\Component\DependencyInjection\Reference;
2423
use Symfony\Component\DependencyInjection\TypedReference;
24+
use Symfony\Component\VarExporter\ProxyHelper;
2525

2626
/**
2727
* @author Guilhem Niot <guilhem.niot@gmail.com>
@@ -176,10 +176,11 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
176176
continue;
177177
}
178178

179-
$typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter);
179+
$typeHint = ltrim(ProxyHelper::exportType($parameter) ?? '', '?');
180+
180181
$name = Target::parseName($parameter);
181182

182-
if ($typeHint && \array_key_exists($k = ltrim($typeHint, '\\').' $'.$name, $bindings)) {
183+
if ($typeHint && \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$name, $bindings)) {
183184
$arguments[$key] = $this->getBindingValue($bindings[$k]);
184185

185186
continue;

src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
1515
use Symfony\Component\DependencyInjection\Definition;
1616
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
17-
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
1817
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\VarExporter\ProxyHelper;
1919

2020
/**
2121
* Resolves named arguments to their corresponding numeric index.
@@ -87,7 +87,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
8787

8888
$typeFound = false;
8989
foreach ($parameters as $j => $p) {
90-
if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) {
90+
if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::exportType($p, true) === $key) {
9191
$resolvedArguments[$j] = $argument;
9292
$typeFound = true;
9393
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+1-10
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
4747
use Symfony\Component\ExpressionLanguage\Expression;
4848
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
49-
use Symfony\Component\VarExporter\Hydrator;
5049

5150
/**
5251
* ContainerBuilder is a DI container that provides an API to easily describe services.
@@ -291,7 +290,7 @@ public function addObjectResource(object|string $object): static
291290
{
292291
if ($this->trackResources) {
293292
if (\is_object($object)) {
294-
$object = \get_class($object);
293+
$object = $object::class;
295294
}
296295
if (!isset($this->classReflectors[$object])) {
297296
$this->classReflectors[$object] = new \ReflectionClass($object);
@@ -1037,10 +1036,6 @@ private function createService(Definition $definition, array &$inlineServices, b
10371036
if (null !== $factory) {
10381037
$service = $factory(...$arguments);
10391038

1040-
if (\is_object($tryProxy) && \get_class($service) !== $parameterBag->resolveValue($definition->getClass())) {
1041-
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)));
1042-
}
1043-
10441039
if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
10451040
$r = new \ReflectionClass($factory[0]);
10461041

@@ -1110,10 +1105,6 @@ private function createService(Definition $definition, array &$inlineServices, b
11101105
$callable($service);
11111106
}
11121107

1113-
if (\is_object($tryProxy) && $tryProxy !== $service) {
1114-
return Hydrator::hydrate($tryProxy, (array) $service);
1115-
}
1116-
11171108
return $service;
11181109
}
11191110

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

+4-26
Original file line numberDiff line numberDiff line change
@@ -673,9 +673,6 @@ private function addServiceInstance(string $id, Definition $definition, bool $is
673673

674674
$return = '';
675675
if ($isSimpleInstance) {
676-
if ($asGhostObject && null !== $definition->getFactory()) {
677-
$instantiation .= '$this->hydrateProxy($lazyLoad, ';
678-
}
679676
$return = 'return ';
680677
} else {
681678
$instantiation .= ' = ';
@@ -893,9 +890,7 @@ protected function {$methodName}($lazyInitialization)
893890
$code .= sprintf(' %s ??= ', $factory);
894891

895892
if ($asFile) {
896-
$code .= "function () {\n";
897-
$code .= " return self::do(\$container);\n";
898-
$code .= " };\n\n";
893+
$code .= "fn () => self::do(\$container);\n\n";
899894
} else {
900895
$code .= sprintf("\$this->%s(...);\n\n", $methodName);
901896
}
@@ -1076,11 +1071,7 @@ private function addInlineService(string $id, Definition $definition, Definition
10761071
return $code;
10771072
}
10781073

1079-
if (!$asGhostObject) {
1080-
return $code."\n return \$instance;\n";
1081-
}
1082-
1083-
return $code."\n return \$this->hydrateProxy(\$lazyLoad, \$instance);\n";
1074+
return $code."\n return \$instance;\n";
10841075
}
10851076

10861077
private function addServices(array &$services = null): string
@@ -1326,19 +1317,6 @@ protected function createProxy(\$class, \Closure \$factory)
13261317
{$proxyLoader}return \$factory();
13271318
}
13281319
1329-
protected function hydrateProxy(\$proxy, \$instance)
1330-
{
1331-
if (\$proxy === \$instance) {
1332-
return \$proxy;
1333-
}
1334-
1335-
if (!\in_array(\get_class(\$instance), [\get_class(\$proxy), get_parent_class(\$proxy)], true)) {
1336-
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".', get_parent_class(\$proxy), get_debug_type(\$instance)));
1337-
}
1338-
1339-
return \Symfony\Component\VarExporter\Hydrator::hydrate(\$proxy, (array) \$instance);
1340-
}
1341-
13421320
EOF;
13431321
break;
13441322
}
@@ -1665,7 +1643,7 @@ private function exportParameters(array $parameters, string $path = '', int $ind
16651643
throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key));
16661644
} elseif ($value instanceof \UnitEnum) {
16671645
$hasEnum = true;
1668-
$value = sprintf('\%s::%s', \get_class($value), $value->name);
1646+
$value = sprintf('\%s::%s', $value::class, $value->name);
16691647
} else {
16701648
$value = $this->export($value);
16711649
}
@@ -1917,7 +1895,7 @@ private function dumpValue(mixed $value, bool $interpolate = true): string
19171895
return $code;
19181896
}
19191897
} elseif ($value instanceof \UnitEnum) {
1920-
return sprintf('\%s::%s', \get_class($value), $value->name);
1898+
return sprintf('\%s::%s', $value::class, $value->name);
19211899
} elseif ($value instanceof AbstractArgument) {
19221900
throw new RuntimeException($value->getTextWithContext());
19231901
} elseif (\is_object($value) || \is_resource($value)) {

0 commit comments

Comments
 (0)