Make failed autowiring error messages more explicit

This commit is contained in:
Mathieu Lemoine 2016-05-02 11:12:26 -04:00
parent 39ddd2383f
commit 2ac81f954b
2 changed files with 46 additions and 16 deletions

View File

@ -105,7 +105,7 @@ class AutowirePass implements CompilerPassInterface
$this->populateAvailableTypes(); $this->populateAvailableTypes();
} }
if (isset($this->types[$typeHint->name])) { if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
$value = new Reference($this->types[$typeHint->name]); $value = new Reference($this->types[$typeHint->name]);
} else { } else {
try { try {
@ -190,22 +190,26 @@ class AutowirePass implements CompilerPassInterface
*/ */
private function set($type, $id) private function set($type, $id)
{ {
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) { if (isset($this->definedTypes[$type])) {
return; return;
} }
if (isset($this->types[$type])) { if (!isset($this->types[$type])) {
if ($this->types[$type] === $id) { $this->types[$type] = $id;
return;
}
unset($this->types[$type]); return;
}
if ($this->types[$type] === $id) {
return;
}
if (!isset($this->notGuessableTypes[$type])) {
$this->notGuessableTypes[$type] = true; $this->notGuessableTypes[$type] = true;
$this->types[$type] = (array) $this->types[$type];
return;
} }
$this->types[$type] = $id; $this->types[$type][] = $id;
} }
/** /**
@ -220,8 +224,14 @@ class AutowirePass implements CompilerPassInterface
*/ */
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
{ {
if (isset($this->notGuessableTypes[$typeHint->name]) || !$typeHint->isInstantiable()) { if (isset($this->notGuessableTypes[$typeHint->name])) {
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id)); throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Several services implementing this type have been declared: "%s".', $typeHint->name, $id, implode('", "', $this->types[$typeHint->name])));
}
$noAvailableDefinitionMessage = sprintf('Unable to autowire argument of type "%s" for the service "%s". This type cannot be instantiated automatically and no service implementing this type is declared.', $typeHint->name, $id);
if (!$typeHint->isInstantiable()) {
throw new RuntimeException($noAvailableDefinitionMessage);
} }
$argumentId = sprintf('autowired.%s', $typeHint->name); $argumentId = sprintf('autowired.%s', $typeHint->name);
@ -230,7 +240,12 @@ class AutowirePass implements CompilerPassInterface
$argumentDefinition->setPublic(false); $argumentDefinition->setPublic(false);
$this->populateAvailableType($argumentId, $argumentDefinition); $this->populateAvailableType($argumentId, $argumentDefinition);
$this->completeDefinition($argumentId, $argumentDefinition);
try {
$this->completeDefinition($argumentId, $argumentDefinition);
} catch (RuntimeException $e) {
throw new RuntimeException($noAvailableDefinitionMessage, 0, $e);
}
return new Reference($argumentId); return new Reference($argumentId);
} }

View File

@ -103,7 +103,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Several services implementing this type have been declared: "c1", "c2".
*/ */
public function testTypeCollision() public function testTypeCollision()
{ {
@ -120,7 +120,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Several services implementing this type have been declared: "a1", "a2".
*/ */
public function testTypeNotGuessable() public function testTypeNotGuessable()
{ {
@ -137,7 +137,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
/** /**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Several services implementing this type have been declared: "a1", "a2".
*/ */
public function testTypeNotGuessableWithSubclass() public function testTypeNotGuessableWithSubclass()
{ {
@ -207,6 +207,21 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
} }
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". This type cannot be instantiated automatically and no service implementing this type is declared.
*/
public function testCreateNonInstanciable()
{
$container = new ContainerBuilder();
$aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
$aDefinition->setAutowired(true);
$pass = new AutowirePass();
$pass->process($container);
}
public function testResolveParameter() public function testResolveParameter()
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();