Skip to content

[DependencyInjection][VarExporter] Generate lazy-loading virtual proxies for non-ghostable lazy services #47236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions .github/expected-missing-return-types.diff
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff)
git checkout composer.json src/

diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 165797504b..0c0922088a 100644
index 18b5c21b9f..8fca8244e3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -87,5 +87,5 @@ abstract class KernelTestCase extends TestCase
@@ -88,5 +88,5 @@ abstract class KernelTestCase extends TestCase
* @return Container
*/
- protected static function getContainer(): ContainerInterface
Expand Down Expand Up @@ -156,52 +156,52 @@ index 6b1c6c5fbe..bb80ed461e 100644
+ public function isFresh(ResourceInterface $resource, int $timestamp): bool;
}
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 64068fcc23..f29aaf1b94 100644
index 53f2021fa9..cf95c1fe99 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -218,5 +218,5 @@ class Application implements ResetInterface
@@ -219,5 +219,5 @@ class Application implements ResetInterface
* @return int 0 if everything went fine, or an error code
*/
- public function doRun(InputInterface $input, OutputInterface $output)
+ public function doRun(InputInterface $input, OutputInterface $output): int
{
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
@@ -454,5 +454,5 @@ class Application implements ResetInterface
@@ -453,5 +453,5 @@ class Application implements ResetInterface
* @return string
*/
- public function getLongVersion()
+ public function getLongVersion(): string
{
if ('UNKNOWN' !== $this->getName()) {
@@ -497,5 +497,5 @@ class Application implements ResetInterface
@@ -496,5 +496,5 @@ class Application implements ResetInterface
* @return Command|null
*/
- public function add(Command $command)
+ public function add(Command $command): ?Command
{
$this->init();
@@ -534,5 +534,5 @@ class Application implements ResetInterface
@@ -533,5 +533,5 @@ class Application implements ResetInterface
* @throws CommandNotFoundException When given command name does not exist
*/
- public function get(string $name)
+ public function get(string $name): Command
{
$this->init();
@@ -641,5 +641,5 @@ class Application implements ResetInterface
@@ -640,5 +640,5 @@ class Application implements ResetInterface
* @throws CommandNotFoundException When command name is incorrect or ambiguous
*/
- public function find(string $name)
+ public function find(string $name): Command
{
$this->init();
@@ -751,5 +751,5 @@ class Application implements ResetInterface
@@ -750,5 +750,5 @@ class Application implements ResetInterface
* @return Command[]
*/
- public function all(string $namespace = null)
+ public function all(string $namespace = null): array
{
$this->init();
@@ -950,5 +950,5 @@ class Application implements ResetInterface
@@ -949,5 +949,5 @@ class Application implements ResetInterface
* @return int 0 if everything went fine, or an error code
*/
- protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
Expand Down Expand Up @@ -234,7 +234,7 @@ index b41e691537..34de10fa70 100644
{
if (null === $this->helperSet) {
diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
index 3c6b0efccd..121664f15a 100644
index 0112350a50..dc972564fb 100644
--- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php
+++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php
@@ -137,5 +137,5 @@ class OutputFormatter implements WrappableOutputFormatterInterface
Expand Down Expand Up @@ -301,22 +301,22 @@ index 2f1631ed30..a4b572771e 100644
{
if (\is_array($value)) {
diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
index 04b7022484..5d736ec754 100644
index 20ca68e514..e0850df0f8 100644
--- a/src/Symfony/Component/DependencyInjection/Container.php
+++ b/src/Symfony/Component/DependencyInjection/Container.php
@@ -108,5 +108,5 @@ class Container implements ContainerInterface, ResetInterface
* @throws InvalidArgumentException if the parameter is not defined
@@ -109,5 +109,5 @@ class Container implements ContainerInterface, ResetInterface
* @throws ParameterNotFoundException if the parameter is not defined
*/
- public function getParameter(string $name)
+ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
{
return $this->parameterBag->get($name);
diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
index cad44026c0..14cd192e07 100644
index 9e97fb71fc..1cda97c611 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
@@ -53,5 +53,5 @@ interface ContainerInterface extends PsrContainerInterface
* @throws InvalidArgumentException if the parameter is not defined
* @throws ParameterNotFoundException if the parameter is not defined
*/
- public function getParameter(string $name);
+ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null;
Expand Down Expand Up @@ -358,7 +358,7 @@ index d553203c43..1163f4b107 100644
{
$class = static::class;
diff --git a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
index 4f66f18073..e96d867296 100644
index 11cda00cc5..07b4990160 100644
--- a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
+++ b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
@@ -35,5 +35,5 @@ interface ExtensionInterface
Expand Down Expand Up @@ -913,7 +913,7 @@ index 5014b9bd51..757c76f546 100644
+ public function supportsDecoding(string $format): bool;
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
index 391cdcb39c..f637687e74 100644
index e426d87076..350cbc6335 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
@@ -213,5 +213,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
Expand Down Expand Up @@ -1094,11 +1094,11 @@ index b22d6ae609..31d1a25f9d 100644
+ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic): array
{
$refs = $values;
diff --git a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
index 471c1a6b91..2e19d2ab2d 100644
--- a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
+++ b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php
@@ -54,5 +54,5 @@ class GhostObjectState
diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
index 6ea89bc831..01748bcfc0 100644
--- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
+++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
@@ -56,5 +56,5 @@ class LazyObjectState
* @return bool Returns true when fully-initializing, false when partial-initializing
*/
- public function initialize($instance, $propertyName, $propertyScope)
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Bridge/Doctrine/ManagerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\Proxy\LazyLoadingInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\VarExporter\LazyGhostObjectInterface;
use Symfony\Component\VarExporter\LazyObjectInterface;

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPUnit\Framework\TestCase;
use ProxyManager\Proxy\LazyLoadingInterface;
use ProxyManagerBridgeFooClass;
use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
Expand All @@ -29,6 +30,7 @@ class ContainerBuilderTest extends TestCase
public function testCreateProxyServiceWithRuntimeInstantiator()
{
$builder = new ContainerBuilder();
$builder->setProxyInstantiator(new RuntimeInstantiator());

$builder->register('foo1', ProxyManagerBridgeFooClass::class)->setFile(__DIR__.'/Fixtures/includes/foo.php')->setPublic(true);
$builder->getDefinition('foo1')->setLazy(true)->addTag('proxy', ['interface' => ProxyManagerBridgeFooClass::class]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use ProxyManager\Proxy\LazyLoadingInterface;
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;

Expand Down Expand Up @@ -66,6 +67,7 @@ private function dumpLazyServiceProjectServiceContainer()
$container->compile();

$dumper = new PhpDumper($container);
$dumper->setProxyDumper(new ProxyDumper());

return $dumper->dump(['class' => 'LazyServiceProjectServiceContainer']);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CHANGELOG
6.2
---

* Use lazy-loading ghost object proxies out of the box
* Use lazy-loading ghost objects and virtual proxies out of the box
* Add argument `&$asGhostObject` to LazyProxy's `DumperInterface` to allow using ghost objects for lazy loading services
* Add `enum` env var processor
* Add `shuffle` env var processor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\VarExporter\ProxyHelper;
use Symfony\Contracts\Service\Attribute\SubscribedService;

/**
Expand Down Expand Up @@ -276,7 +276,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
continue;
}

$type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
$type = ProxyHelper::exportType($parameter, true);

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

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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\VarExporter\ProxyHelper;

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

$typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter);
$typeHint = ltrim(ProxyHelper::exportType($parameter) ?? '', '?');

$name = Target::parseName($parameter);

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

continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\VarExporter\ProxyHelper;

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

$typeFound = false;
foreach ($parameters as $j => $p) {
if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) {
if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::exportType($p, true) === $key) {
$resolvedArguments[$j] = $argument;
$typeFound = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
use Symfony\Component\VarExporter\Hydrator;

/**
* ContainerBuilder is a DI container that provides an API to easily describe services.
Expand Down Expand Up @@ -1037,10 +1036,6 @@ private function createService(Definition $definition, array &$inlineServices, b
if (null !== $factory) {
$service = $factory(...$arguments);

if (\is_object($tryProxy) && $service::class !== $parameterBag->resolveValue($definition->getClass())) {
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)));
}

if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
$r = new \ReflectionClass($factory[0]);

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

if (\is_object($tryProxy) && $tryProxy !== $service) {
return Hydrator::hydrate($tryProxy, (array) $service);
}

return $service;
}

Expand Down
26 changes: 2 additions & 24 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -673,9 +673,6 @@ private function addServiceInstance(string $id, Definition $definition, bool $is

$return = '';
if ($isSimpleInstance) {
if ($asGhostObject && null !== $definition->getFactory()) {
$instantiation .= '$this->hydrateProxy($lazyLoad, ';
}
$return = 'return ';
} else {
$instantiation .= ' = ';
Expand Down Expand Up @@ -893,9 +890,7 @@ protected function {$methodName}($lazyInitialization)
$code .= sprintf(' %s ??= ', $factory);

if ($asFile) {
$code .= "function () {\n";
$code .= " return self::do(\$container);\n";
$code .= " };\n\n";
$code .= "fn () => self::do(\$container);\n\n";
} else {
$code .= sprintf("\$this->%s(...);\n\n", $methodName);
}
Expand Down Expand Up @@ -1076,11 +1071,7 @@ private function addInlineService(string $id, Definition $definition, Definition
return $code;
}

if (!$asGhostObject) {
return $code."\n return \$instance;\n";
}

return $code."\n return \$this->hydrateProxy(\$lazyLoad, \$instance);\n";
return $code."\n return \$instance;\n";
}

private function addServices(array &$services = null): string
Expand Down Expand Up @@ -1326,19 +1317,6 @@ protected function createProxy(\$class, \Closure \$factory)
{$proxyLoader}return \$factory();
}

protected function hydrateProxy(\$proxy, \$instance)
{
if (\$proxy === \$instance) {
return \$proxy;
}

if (!\in_array(\get_class(\$instance), [\get_class(\$proxy), get_parent_class(\$proxy)], true)) {
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)));
}

return \Symfony\Component\VarExporter\Hydrator::hydrate(\$proxy, (array) \$instance);
}

EOF;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@

namespace Symfony\Component\DependencyInjection\LazyProxy\Instantiator;

use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper;
use Symfony\Component\VarExporter\LazyGhostObjectInterface;
use Symfony\Component\VarExporter\LazyGhostObjectTrait;
use Symfony\Component\VarExporter\LazyGhostTrait;

/**
* @author Nicolas Grekas <p@tchwork.com>
Expand All @@ -27,14 +25,10 @@ public function instantiateProxy(ContainerInterface $container, Definition $defi
{
$dumper = new LazyServiceDumper();

if ($dumper->useProxyManager($definition)) {
return (new RuntimeInstantiator())->instantiateProxy($container, $definition, $id, $realInstantiator);
if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $class), false)) {
eval($dumper->getProxyCode($definition));
}

if (!class_exists($proxyClass = $dumper->getProxyClass($definition), false)) {
eval(sprintf('class %s extends %s implements %s { use %s; }', $proxyClass, $definition->getClass(), LazyGhostObjectInterface::class, LazyGhostObjectTrait::class));
}

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