Merge branch '2.8' into 3.0

* 2.8:
  [EventDispatcher] check for method to exist
  [DependencyInjection] Fixed the priority of service decoration on service with parent
  Make failed autowiring error messages more explicit
This commit is contained in:
Fabien Potencier 2016-05-03 20:59:52 +02:00
commit 2100ecdcc2
6 changed files with 65 additions and 19 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

@ -156,7 +156,7 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
if (null === $decoratedService) { if (null === $decoratedService) {
$def->setDecoratedService($decoratedService); $def->setDecoratedService($decoratedService);
} else { } else {
$def->setDecoratedService($decoratedService[0], $decoratedService[1]); $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]);
} }
} }

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();

View File

@ -253,10 +253,12 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
$container->register('parent', 'stdClass'); $container->register('parent', 'stdClass');
$container->setDefinition('child1', new DefinitionDecorator('parent')) $container->setDefinition('child1', new DefinitionDecorator('parent'))
->setDecoratedService('foo', 'foo_inner') ->setDecoratedService('foo', 'foo_inner', 5)
; ;
$this->assertEquals(array('foo', 'foo_inner', 0), $container->getDefinition('child1')->getDecoratedService()); $this->process($container);
$this->assertEquals(array('foo', 'foo_inner', 5), $container->getDefinition('child1')->getDecoratedService());
} }
public function testDecoratedServiceCopiesDeprecatedStatusFromParent() public function testDecoratedServiceCopiesDeprecatedStatusFromParent()

View File

@ -104,6 +104,10 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
*/ */
public function getListenerPriority($eventName, $listener) public function getListenerPriority($eventName, $listener)
{ {
if (!method_exists($this->dispatcher, 'getListenerPriority')) {
return 0;
}
return $this->dispatcher->getListenerPriority($eventName, $listener); return $this->dispatcher->getListenerPriority($eventName, $listener);
} }

View File

@ -73,6 +73,16 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
} }
public function testGetListenerPriorityReturnsZeroWhenWrappedMethodDoesNotExist()
{
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$traceableEventDispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
$traceableEventDispatcher->addListener('foo', function () {}, 123);
$listeners = $traceableEventDispatcher->getListeners('foo');
$this->assertSame(0, $traceableEventDispatcher->getListenerPriority('foo', $listeners[0]));
}
public function testAddRemoveSubscriber() public function testAddRemoveSubscriber()
{ {
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();