merged branch fabpot/event-services-check (PR #9006)

This PR was merged into the 2.2 branch.

Discussion
----------

Fixed registration of private event listeners/subscribers

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #6541
| License       | MIT
| Doc PR        | n/a

When registering a private event subscriber/listener, the listener was just ignored.

This PR does two things to fix the problem:

 * the event listener pass is done earlier to get access to the private services
 * when a private service is identified, a proper exception is thrown

Commits
-------

8444339 [HttpKernel] added a check for private event listeners/subscribers
427ee19 [FrameworkBundle] fixed registration of the register listener pass
This commit is contained in:
Fabien Potencier 2013-09-12 14:49:21 +02:00
commit 6c10b6966d
3 changed files with 48 additions and 2 deletions

View File

@ -25,6 +25,11 @@ class RegisterKernelListenersPass implements CompilerPassInterface
$definition = $container->getDefinition('event_dispatcher');
foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $events) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
}
foreach ($events as $event) {
$priority = isset($event['priority']) ? $event['priority'] : 0;
@ -45,8 +50,13 @@ class RegisterKernelListenersPass implements CompilerPassInterface
}
foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
}
// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $container->getDefinition($id)->getClass();
$class = $def->getClass();
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';

View File

@ -60,7 +60,9 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new RoutingResolverPass());
$container->addCompilerPass(new ProfilerPass());
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
// must be registered before removing private services as some might be listeners/subscribers
// but as late as possible to get resolved parameters
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new TemplatingPass());
$container->addCompilerPass(new AddConstraintValidatorsPass());
$container->addCompilerPass(new AddValidatorInitializersPass());

View File

@ -31,6 +31,9 @@ class RegisterKernelListenersPassTest extends \PHPUnit_Framework_TestCase
);
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
$definition->expects($this->atLeastOnce())
->method('isPublic')
->will($this->returnValue(true));
$definition->expects($this->atLeastOnce())
->method('getClass')
->will($this->returnValue('stdClass'));
@ -60,6 +63,9 @@ class RegisterKernelListenersPassTest extends \PHPUnit_Framework_TestCase
);
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
$definition->expects($this->atLeastOnce())
->method('isPublic')
->will($this->returnValue(true));
$definition->expects($this->atLeastOnce())
->method('getClass')
->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\SubscriberService'));
@ -81,6 +87,34 @@ class RegisterKernelListenersPassTest extends \PHPUnit_Framework_TestCase
$registerListenersPass = new RegisterKernelListenersPass();
$registerListenersPass->process($builder);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded.
*/
public function testPrivateEventListener()
{
$container = new ContainerBuilder();
$container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array());
$container->register('event_dispatcher', 'stdClass');
$registerListenersPass = new RegisterKernelListenersPass();
$registerListenersPass->process($container);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded.
*/
public function testPrivateEventSubscriber()
{
$container = new ContainerBuilder();
$container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array());
$container->register('event_dispatcher', 'stdClass');
$registerListenersPass = new RegisterKernelListenersPass();
$registerListenersPass->process($container);
}
}
class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface