Skip to content

Commit

Permalink
bug #21937 [DependencyInjection] Handle void return types in closure-…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
nicolas-grekas committed Mar 10, 2017
2 parents 64f9f7b + a5c5ad1 commit b77d97a
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 1 deletion.
Expand Up @@ -1656,7 +1656,9 @@ private function dumpValue($value, $interpolate = true)
}
$signature = preg_replace('/^(&?)[^(]*/', '$1', InheritanceProxyHelper::getSignature($r, $call));

return sprintf("/** @closure-proxy %s::%s */ function %s {\n return %s->%s;\n }", $class, $method, $signature, $this->dumpValue($reference), $call);
$return = 'void' !== InheritanceProxyHelper::getTypeHint($r);

return sprintf("/** @closure-proxy %s::%s */ function %s {\n %s%s->%s;\n }", $class, $method, $signature, $return ? 'return ' : '', $this->dumpValue($reference), $call);
} elseif ($value instanceof Variable) {
return '$'.$value;
} elseif ($value instanceof Reference) {
Expand Down
Expand Up @@ -614,6 +614,21 @@ public function testClosureProxy()
$this->assertSame('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Container31\Foo', (string) array_pop($res));
}

/**
* @requires PHP 7.1
*/
public function testClosureProxyWithVoidReturnType()
{
$container = include self::$fixturesPath.'/containers/container_dump_proxy_with_void_return_type.php';

$container->compile();
$dumper = new PhpDumper($container);

$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dump_proxy_with_void_return_type.php', $dumper->dump());
$res = $container->getResources();
$this->assertSame('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo', (string) array_pop($res));
}

/**
* @requires PHP 7.1
*/
Expand Down
@@ -0,0 +1,27 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid;

use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;

if (!class_exists(Foo::class, false)) {
class Foo
{
public function withVoid(): void
{
}
}
}

$container = new ContainerBuilder();

$container->register('foo', Foo::class);

$container->register('bar', 'stdClass')
->setProperty('foo', array(
new ClosureProxyArgument('foo', 'withVoid'),
))
;

return $container;
@@ -0,0 +1,91 @@
<?php

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
use Symfony\Component\DependencyInjection\ServiceLocator;

/**
* ProjectServiceContainer.
*
* This class has been auto-generated
* by the Symfony Dependency Injection Component.
*
* @final since Symfony 3.3
*/
class ProjectServiceContainer extends Container
{
private $parameters;
private $targetDirs = array();

/**
* Constructor.
*/
public function __construct()
{
$this->services = array();
$this->normalizedIds = array(
'psr\\container\\containerinterface' => 'Psr\\Container\\ContainerInterface',
'symfony\\component\\dependencyinjection\\container' => 'Symfony\\Component\\DependencyInjection\\Container',
'symfony\\component\\dependencyinjection\\containerinterface' => 'Symfony\\Component\\DependencyInjection\\ContainerInterface',
);
$this->methodMap = array(
'bar' => 'getBarService',
'foo' => 'getFooService',
);

$this->aliases = array();
}

/**
* {@inheritdoc}
*/
public function compile()
{
throw new LogicException('You cannot compile a dumped frozen container.');
}

/**
* {@inheritdoc}
*/
public function isFrozen()
{
return true;
}

/**
* Gets the 'bar' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \stdClass A stdClass instance
*/
protected function getBarService()
{
$this->services['bar'] = $instance = new \stdClass();

$instance->foo = array(0 => /** @closure-proxy Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo::withVoid */ function (): void {
${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->get('foo')) && false ?: '_'}->withVoid();
});

return $instance;
}

/**
* Gets the 'foo' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo A Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo instance
*/
protected function getFooService()
{
return $this->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\ContainerVoid\Foo();
}
}

0 comments on commit b77d97a

Please sign in to comment.