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();
}
if (isset($this->types[$typeHint->name])) {
if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
$value = new Reference($this->types[$typeHint->name]);
} else {
try {
@ -190,22 +190,26 @@ class AutowirePass implements CompilerPassInterface
*/
private function set($type, $id)
{
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) {
if (isset($this->definedTypes[$type])) {
return;
}
if (isset($this->types[$type])) {
if ($this->types[$type] === $id) {
return;
}
if (!isset($this->types[$type])) {
$this->types[$type] = $id;
unset($this->types[$type]);
return;
}
if ($this->types[$type] === $id) {
return;
}
if (!isset($this->notGuessableTypes[$type])) {
$this->notGuessableTypes[$type] = true;
return;
$this->types[$type] = (array) $this->types[$type];
}
$this->types[$type] = $id;
$this->types[$type][] = $id;
}
/**
@ -220,8 +224,14 @@ class AutowirePass implements CompilerPassInterface
*/
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
{
if (isset($this->notGuessableTypes[$typeHint->name]) || !$typeHint->isInstantiable()) {
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id));
if (isset($this->notGuessableTypes[$typeHint->name])) {
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);
@ -230,7 +240,12 @@ class AutowirePass implements CompilerPassInterface
$argumentDefinition->setPublic(false);
$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);
}

View File

@ -156,7 +156,7 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
if (null === $decoratedService) {
$def->setDecoratedService($decoratedService);
} 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
* @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()
{
@ -120,7 +120,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
/**
* @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()
{
@ -137,7 +137,7 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
/**
* @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()
{
@ -207,6 +207,21 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
$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()
{
$container = new ContainerBuilder();

View File

@ -253,10 +253,12 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
$container->register('parent', 'stdClass');
$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()

View File

@ -104,6 +104,10 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
*/
public function getListenerPriority($eventName, $listener)
{
if (!method_exists($this->dispatcher, 'getListenerPriority')) {
return 0;
}
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]));
}
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()
{
$dispatcher = new EventDispatcher();