diff --git a/src/Symfony/Component/DependencyInjection/Argument/LazyClosure.php b/src/Symfony/Component/DependencyInjection/Argument/LazyClosure.php index 230363a95bf3a..45e1c9d56c58e 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/LazyClosure.php +++ b/src/Symfony/Component/DependencyInjection/Argument/LazyClosure.php @@ -40,22 +40,22 @@ public function __get(mixed $name): mixed } if (isset($this->initializer)) { - $this->service = ($this->initializer)(); + if (\is_string($service = ($this->initializer)())) { + $service = (new \ReflectionClass($service))->newInstanceWithoutConstructor(); + } + $this->service = $service; unset($this->initializer); } return $this->service; } - public static function getCode(string $initializer, array $callable, Definition $definition, ContainerBuilder $container, ?string $id): string + public static function getCode(string $initializer, array $callable, string $class, ContainerBuilder $container, ?string $id): string { $method = $callable[1]; - $asClosure = 'Closure' === ($definition->getClass() ?: 'Closure'); - if ($asClosure) { + if ($asClosure = 'Closure' === $class) { $class = ($callable[0] instanceof Reference ? $container->findDefinition($callable[0]) : $callable[0])->getClass(); - } else { - $class = $definition->getClass(); } $r = $container->getReflectionClass($class); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 2771defe45134..4dc7c4e231e2e 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1060,14 +1060,15 @@ private function createService(Definition $definition, array &$inlineServices, b } if (\is_array($callable) && ( - $callable[0] instanceof Reference + 'Closure' !== $class + || $callable[0] instanceof Reference || $callable[0] instanceof Definition && !isset($inlineServices[spl_object_hash($callable[0])]) )) { $initializer = function () use ($callable, &$inlineServices) { return $this->doResolveServices($callable[0], $inlineServices); }; - $proxy = eval('return '.LazyClosure::getCode('$initializer', $callable, $definition, $this, $id).';'); + $proxy = eval('return '.LazyClosure::getCode('$initializer', $callable, $class, $this, $id).';'); $this->shareService($definition, $proxy, $id, $inlineServices); return $proxy; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index bdb95691354ca..164dddf202077 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1202,13 +1202,13 @@ private function addNewInstance(Definition $definition, string $return = '', ?st throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a')); } - if (['...'] === $arguments && ($definition->isLazy() || 'Closure' !== ($definition->getClass() ?? 'Closure')) && ( + if (['...'] === $arguments && ('Closure' !== ($class = $definition->getClass() ?: 'Closure') || $definition->isLazy() && ( $callable[0] instanceof Reference || ($callable[0] instanceof Definition && !$this->definitionVariables->contains($callable[0])) - )) { + ))) { $initializer = 'fn () => '.$this->dumpValue($callable[0]); - return $return.LazyClosure::getCode($initializer, $callable, $definition, $this->container, $id).$tail; + return $return.LazyClosure::getCode($initializer, $callable, $class, $this->container, $id).$tail; } if ($callable[0] instanceof Reference diff --git a/src/Symfony/Component/DependencyInjection/Tests/Argument/LazyClosureTest.php b/src/Symfony/Component/DependencyInjection/Tests/Argument/LazyClosureTest.php index 46ef1591785cf..9652a86fd24b6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Argument/LazyClosureTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Argument/LazyClosureTest.php @@ -34,7 +34,7 @@ public function testThrowsWhenNotUsingInterface() $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Cannot create adapter for service "foo" because "Symfony\Component\DependencyInjection\Tests\Argument\LazyClosureTest" is not an interface.'); - LazyClosure::getCode('foo', [new \stdClass(), 'bar'], new Definition(LazyClosureTest::class), new ContainerBuilder(), 'foo'); + LazyClosure::getCode('foo', [new \stdClass(), 'bar'], LazyClosureTest::class, new ContainerBuilder(), 'foo'); } public function testThrowsOnNonFunctionalInterface() @@ -42,7 +42,7 @@ public function testThrowsOnNonFunctionalInterface() $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Cannot create adapter for service "foo" because interface "Symfony\Component\DependencyInjection\Tests\Argument\NonFunctionalInterface" doesn\'t have exactly one method.'); - LazyClosure::getCode('foo', [new \stdClass(), 'bar'], new Definition(NonFunctionalInterface::class), new ContainerBuilder(), 'foo'); + LazyClosure::getCode('foo', [new \stdClass(), 'bar'], NonFunctionalInterface::class, new ContainerBuilder(), 'foo'); } public function testThrowsOnUnknownMethodInInterface() @@ -50,7 +50,7 @@ public function testThrowsOnUnknownMethodInInterface() $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Cannot create lazy closure for service "bar" because its corresponding callable is invalid.'); - LazyClosure::getCode('bar', [new Definition(FunctionalInterface::class), 'bar'], new Definition(\Closure::class), new ContainerBuilder(), 'bar'); + LazyClosure::getCode('bar', [new Definition(FunctionalInterface::class), 'bar'], \Closure::class, new ContainerBuilder(), 'bar'); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 85693bec0b27c..f072a4ee82d83 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -49,6 +49,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Tests\Compiler\Foo; use Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation; +use Symfony\Component\DependencyInjection\Tests\Compiler\MyCallable; use Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface; use Symfony\Component\DependencyInjection\Tests\Compiler\Wither; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; @@ -522,6 +523,19 @@ public function testClosureProxy() $this->assertInstanceOf(Foo::class, $container->get('closure_proxy')->theMethod()); } + public function testClosureProxyWithStaticMethod() + { + $container = new ContainerBuilder(); + $container->register('closure_proxy', SingleMethodInterface::class) + ->setPublic('true') + ->setFactory(['Closure', 'fromCallable']) + ->setArguments([[MyCallable::class, 'theMethodImpl']]); + $container->compile(); + + $this->assertInstanceOf(SingleMethodInterface::class, $container->get('closure_proxy')); + $this->assertSame(124, $container->get('closure_proxy')->theMethod()); + } + public function testCreateServiceClass() { $builder = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index a9ac5c0bff430..efbcc8d1986c1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -582,4 +582,9 @@ class MyCallable public function __invoke(): void { } + + public static function theMethodImpl(): int + { + return 124; + } }