Skip to content

Commit 26e3fc7

Browse files
bug #60842 [DependencyInjection] Fix generating adapters of functional interfaces (nicolas-grekas)
This PR was merged into the 6.4 branch. Discussion ---------- [DependencyInjection] Fix generating adapters of functional interfaces | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | - | License | MIT Fixes support for generating adapters from static method, and also from inline services when they're put in a temporary variable. Commits ------- 86a4445 [DependencyInjection] Fix generating adapters of functional interfaces
2 parents 94c2b34 + 86a4445 commit 26e3fc7

File tree

6 files changed

+34
-14
lines changed

6 files changed

+34
-14
lines changed

src/Symfony/Component/DependencyInjection/Argument/LazyClosure.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,22 @@ public function __get(mixed $name): mixed
4040
}
4141

4242
if (isset($this->initializer)) {
43-
$this->service = ($this->initializer)();
43+
if (\is_string($service = ($this->initializer)())) {
44+
$service = (new \ReflectionClass($service))->newInstanceWithoutConstructor();
45+
}
46+
$this->service = $service;
4447
unset($this->initializer);
4548
}
4649

4750
return $this->service;
4851
}
4952

50-
public static function getCode(string $initializer, array $callable, Definition $definition, ContainerBuilder $container, ?string $id): string
53+
public static function getCode(string $initializer, array $callable, string $class, ContainerBuilder $container, ?string $id): string
5154
{
5255
$method = $callable[1];
53-
$asClosure = 'Closure' === ($definition->getClass() ?: 'Closure');
5456

55-
if ($asClosure) {
57+
if ($asClosure = 'Closure' === $class) {
5658
$class = ($callable[0] instanceof Reference ? $container->findDefinition($callable[0]) : $callable[0])->getClass();
57-
} else {
58-
$class = $definition->getClass();
5959
}
6060

6161
$r = $container->getReflectionClass($class);

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,14 +1060,15 @@ private function createService(Definition $definition, array &$inlineServices, b
10601060
}
10611061

10621062
if (\is_array($callable) && (
1063-
$callable[0] instanceof Reference
1063+
'Closure' !== $class
1064+
|| $callable[0] instanceof Reference
10641065
|| $callable[0] instanceof Definition && !isset($inlineServices[spl_object_hash($callable[0])])
10651066
)) {
10661067
$initializer = function () use ($callable, &$inlineServices) {
10671068
return $this->doResolveServices($callable[0], $inlineServices);
10681069
};
10691070

1070-
$proxy = eval('return '.LazyClosure::getCode('$initializer', $callable, $definition, $this, $id).';');
1071+
$proxy = eval('return '.LazyClosure::getCode('$initializer', $callable, $class, $this, $id).';');
10711072
$this->shareService($definition, $proxy, $id, $inlineServices);
10721073

10731074
return $proxy;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,13 +1202,13 @@ private function addNewInstance(Definition $definition, string $return = '', ?st
12021202
throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a'));
12031203
}
12041204

1205-
if (['...'] === $arguments && ($definition->isLazy() || 'Closure' !== ($definition->getClass() ?? 'Closure')) && (
1205+
if (['...'] === $arguments && ('Closure' !== ($class = $definition->getClass() ?: 'Closure') || $definition->isLazy() && (
12061206
$callable[0] instanceof Reference
12071207
|| ($callable[0] instanceof Definition && !$this->definitionVariables->contains($callable[0]))
1208-
)) {
1208+
))) {
12091209
$initializer = 'fn () => '.$this->dumpValue($callable[0]);
12101210

1211-
return $return.LazyClosure::getCode($initializer, $callable, $definition, $this->container, $id).$tail;
1211+
return $return.LazyClosure::getCode($initializer, $callable, $class, $this->container, $id).$tail;
12121212
}
12131213

12141214
if ($callable[0] instanceof Reference

src/Symfony/Component/DependencyInjection/Tests/Argument/LazyClosureTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,23 @@ public function testThrowsWhenNotUsingInterface()
3434
$this->expectException(\RuntimeException::class);
3535
$this->expectExceptionMessage('Cannot create adapter for service "foo" because "Symfony\Component\DependencyInjection\Tests\Argument\LazyClosureTest" is not an interface.');
3636

37-
LazyClosure::getCode('foo', [new \stdClass(), 'bar'], new Definition(LazyClosureTest::class), new ContainerBuilder(), 'foo');
37+
LazyClosure::getCode('foo', [new \stdClass(), 'bar'], LazyClosureTest::class, new ContainerBuilder(), 'foo');
3838
}
3939

4040
public function testThrowsOnNonFunctionalInterface()
4141
{
4242
$this->expectException(\RuntimeException::class);
4343
$this->expectExceptionMessage('Cannot create adapter for service "foo" because interface "Symfony\Component\DependencyInjection\Tests\Argument\NonFunctionalInterface" doesn\'t have exactly one method.');
4444

45-
LazyClosure::getCode('foo', [new \stdClass(), 'bar'], new Definition(NonFunctionalInterface::class), new ContainerBuilder(), 'foo');
45+
LazyClosure::getCode('foo', [new \stdClass(), 'bar'], NonFunctionalInterface::class, new ContainerBuilder(), 'foo');
4646
}
4747

4848
public function testThrowsOnUnknownMethodInInterface()
4949
{
5050
$this->expectException(\RuntimeException::class);
5151
$this->expectExceptionMessage('Cannot create lazy closure for service "bar" because its corresponding callable is invalid.');
5252

53-
LazyClosure::getCode('bar', [new Definition(FunctionalInterface::class), 'bar'], new Definition(\Closure::class), new ContainerBuilder(), 'bar');
53+
LazyClosure::getCode('bar', [new Definition(FunctionalInterface::class), 'bar'], \Closure::class, new ContainerBuilder(), 'bar');
5454
}
5555
}
5656

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
use Symfony\Component\DependencyInjection\ServiceLocator;
5050
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
5151
use Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation;
52+
use Symfony\Component\DependencyInjection\Tests\Compiler\MyCallable;
5253
use Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface;
5354
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
5455
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
@@ -522,6 +523,19 @@ public function testClosureProxy()
522523
$this->assertInstanceOf(Foo::class, $container->get('closure_proxy')->theMethod());
523524
}
524525

526+
public function testClosureProxyWithStaticMethod()
527+
{
528+
$container = new ContainerBuilder();
529+
$container->register('closure_proxy', SingleMethodInterface::class)
530+
->setPublic('true')
531+
->setFactory(['Closure', 'fromCallable'])
532+
->setArguments([[MyCallable::class, 'theMethodImpl']]);
533+
$container->compile();
534+
535+
$this->assertInstanceOf(SingleMethodInterface::class, $container->get('closure_proxy'));
536+
$this->assertSame(124, $container->get('closure_proxy')->theMethod());
537+
}
538+
525539
public function testCreateServiceClass()
526540
{
527541
$builder = new ContainerBuilder();

src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,9 @@ class MyCallable
582582
public function __invoke(): void
583583
{
584584
}
585+
586+
public static function theMethodImpl(): int
587+
{
588+
return 124;
589+
}
585590
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy