8000 bug #21937 [DependencyInjection] Handle void return types in closure-… · symfony/symfony@b77d97a · GitHub
[go: up one dir, main page]

Skip to content

Commit b77d97a

Browse files
bug #21937 [DependencyInjection] Handle void return types in closure-proxy (pierredup)
This PR was squashed before being merged into the 3.3-dev branch (closes #21937). Discussion ---------- [DependencyInjection] Handle void return types in closure-proxy | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | N/A | License | MIT | Doc PR | N/A I recently got an error when registering an event listener that specifies a `void` return type. Dumping the container generates a closure proxy that always returns a value, which then conflicts with the return type hint. E.G the following code is generated (some class names removed for readability) ``` $instance->addListener('kernel.view', /** @closure-proxy ... */ function (...\GetResponseForControllerResultEvent $event): void { return ${($_ = isset($this->services[listener']) ? $this->services['listener'] : $this->get('listener')) && false ?: '_'}->onKernelView($event); }, 128); ``` This then causes the error `A void function must not return a value in ...` So void return types should be handled by removing the `return` inside the closure Commits ------- a5c5ad1 [DependencyInjection] Handle void return types in closure-proxy
2 parents 64f9f7b + a5c5ad1 commit b77d97a

File tree

4 files changed

+136
-1
lines changed

4 files changed

+136
-1
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,9 @@ private function dumpValue($value, $interpolate = true)
16561656
}
16571657
$signature = preg_replace('/^(&?)[^(]*/', '$1', InheritanceProxyHelper::getSignature($r, $call));
16581658

1659-
return sprintf("/** @closure-proxy %s::%s */ function %s {\n return %s->%s;\n }", $class, $method, $signature, $this->dumpValue($reference), $call);
1659+
$return = 'void' !== InheritanceProxyHelper::getTypeHint($r);
1660+
1661+
return sprintf("/** @closure-proxy %s::%s */ function %s {\n %s%s->%s;\n }", $class, $method, $signature, $return ? 'return ' : '', $this->dumpValue($reference), $call);
16601662
} elseif ($value instanceof Variable) {
16611663
return '$'.$value;
16621664
} elseif ($value instanceof Reference) {

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,21 @@ public function testClosureProxy()
614614
$this->assertSame('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Container31\Foo', (string) array_pop($res));
615615
}
616616

617+
/**
618+
* @requires PHP 7.1
619+
*/
620+
public function testClosureProxyWithVoidReturnType()
621+
{
622+
$container = include self::$fixturesPath.'/containers/container_dump_proxy_with_void_return_type.php';
623+
624+
$container->compile();
625+
$dumper = new PhpDumper($container);
626+
627+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dump_proxy_with_void_return_type.php', $dumper->dump());
628+
$res = $container->getResources();
629+
$this->assertSame('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo', (string) array_pop($res));
630+
}
631+
617632
/**
618633
* @requires PHP 7.1
619634
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid;
4+
5+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
if (!class_exists(Foo::class, false)) {
9+
class Foo
10+
{
11+
public function withVoid(): void
12+
{
13+
}
14+
}
15+
}
16+
17+
$container = new ContainerBuilder();
18+
19+
$container->register('foo', Foo::class);
20+
21+
$container->register('bar', 'stdClass')
22+
->setProperty('foo', array(
23+
new ClosureProxyArgument('foo', 'withVoid'),
24+
))
25+
;
26+
27+
return $container;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
7+
use Symfony\Component\DependencyInjection\Exception\LogicException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
use Symfony\Component\DependencyInjection\ServiceLocator;
11+
12+
/**
13+
* ProjectServiceContainer.
14+
*
15+
* This class has been auto-generated
16+
* by the Symfony Dependency Injection Component.
17+
*
18+
* @final since Symfony 3.3
19+
*/
20+
class ProjectServiceContainer extends Container
21+
{
22+
private $parameters;
23+
private $targetDirs = array();
24+
25+
/**
26+
* Constructor.
27+
*/
28+
public function __construct()
29+
{
30+
$this->services = array();
31+
$this->normalizedIds = array(
32+
'psr\\container\\containerinterface' => 'Psr\\Container\\ContainerInterface',
33+
'symfony\\component\\dependencyinjection\\container' => 'Symfony\\Component\\DependencyInjection\\Container',
34+
'symfony\\component\\dependencyinjection\\containerinterface' => 'Symfony\\Component\\DependencyInjection\\ContainerInterface',
35+
);
36+
$this->methodMap = array(
37+
'bar' => 'getBarService',
38+
'foo' => 'getFooService',
39+
);
40+
41+
$this->aliases = array();
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function compile()
48+
{
49+
throw new LogicException('You cannot compile a dumped frozen container.');
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
public function isFrozen()
56+
{
57+
return true;
58+
}
59+
60+
/**
61+
* Gets the 'bar' service.
62+
*
63+
* This service is shared.
64+
* This method always returns the same instance of the service.
65+
*
66+
* @return \stdClass A stdClass instance
67+
*/
68+
protected function getBarService()
69+
{
70+
$this->services['bar'] = $instance = new \stdClass();
71+
72+
$instance->foo = array(0 => /** @closure-proxy Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo::withVoid */ function (): void {
73+
${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->get('foo')) && false ?: '_'}->withVoid();
74+
});
75+
76+
return $instance;
77+
}
78+
79+
/**
80+
* Gets the 'foo' service.
81+
*
82+
* This service is shared.
83+
* This method always returns the same instance of the service.
84+
*
85+
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo A Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo instance
86+
*/
87+
protected function getFooService()
88+
{
89+
return $this->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo();
90+
}
91+
}

0 commit comments

Comments
 (0)
0