diff --git a/src/DependencyInjection/Compiler/RegisterAutowiredSubscribers.php b/src/DependencyInjection/Compiler/RegisterAutowiredSubscribers.php new file mode 100644 index 0000000..e9f9757 --- /dev/null +++ b/src/DependencyInjection/Compiler/RegisterAutowiredSubscribers.php @@ -0,0 +1,122 @@ +serviceId = $serviceId; + $this->autowiredSubscriberTag = $autowiredSubscriberTag; + $this->manualSubscriberTag = $manualSubscriberTag; + } + + /** + * Search for message autowired subscriber services and provide them as a constructor argument to the message subscriber + * collection service. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + if (!$container->has($this->serviceId)) { + return; + } + + $definition = $container->findDefinition($this->serviceId); + + $handlers = $definition->getArgument(0); + + foreach ($container->findTaggedServiceIds($this->autowiredSubscriberTag) as $serviceId => $tags) { + if (count($tags) > 1) { + throw new RuntimeException( + sprintf('Service "%s" must contain the only "%s" tag.', $serviceId, $this->autowiredSubscriberTag) + ); + } + + $tag = $tags[0]; + + $subscriberDefinition = $container->getDefinition($serviceId); + + if ($subscriberDefinition->hasTag($this->manualSubscriberTag)) { + throw new RuntimeException( + sprintf('Service "%s" must contain either "%s" or "%s" tag, not both of them.', $serviceId, $tag, $this->manualSubscriberTag) + ); + } + + if (!$subscriberDefinition->getClass()) { + throw new RuntimeException( + sprintf('Class is missing in the "%s" service.', $serviceId) + ); + } + + $handlers = array_merge_recursive( + $handlers, + $this->getHandlersFrom(new ReflectionClass($subscriberDefinition->getClass()), $serviceId) + ); + } + + $definition->replaceArgument(0, $handlers); + } + + private function getHandlersFrom(ReflectionClass $class, $serviceId) + { + $handlers = []; + + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if ( + $method->getNumberOfParameters() !== 1 + || !$method->getParameters()[0]->getClass() + ) { + throw new RuntimeException( + sprintf( + 'Method "%s::%s" in service "%s" contains inappropriate public method for autowiring.' + . ' You can try to register your subscribers manually with the "%s" tag.', + $class->getName(), + $method->getName(), + $serviceId, + $this->manualSubscriberTag + ) + ); + } + + $eventClass = $method->getParameters()[0]->getClass(); + + if ($eventClass->implementsInterface(NamedMessage::class)) { + throw new RuntimeException( + sprintf( + 'Event "%s" is incompatible with autowiring feature because it implements the %s in service "%s".' + . ' You can try to register your subscribers manually with the "%s" tag.', + $eventClass->getName(), + NamedMessage::class, + $serviceId, + $this->manualSubscriberTag + ) + ); + } + + $handlers[$eventClass->getName()][] = [$serviceId, $method->getName()]; + } + + return $handlers; + } +} diff --git a/src/DependencyInjection/Compiler/RegisterSubscribers.php b/src/DependencyInjection/Compiler/RegisterSubscribers.php index 6b246ae..ed64415 100644 --- a/src/DependencyInjection/Compiler/RegisterSubscribers.php +++ b/src/DependencyInjection/Compiler/RegisterSubscribers.php @@ -39,7 +39,7 @@ public function process(ContainerBuilder $container) $definition = $container->findDefinition($this->serviceId); - $handlers = array(); + $handlers = $definition->getArgument(0); $this->collectServiceIds( $container, diff --git a/src/SimpleBusEventBusBundle.php b/src/SimpleBusEventBusBundle.php index 6fd6502..042219f 100644 --- a/src/SimpleBusEventBusBundle.php +++ b/src/SimpleBusEventBusBundle.php @@ -5,6 +5,7 @@ use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AddMiddlewareTags; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\CompilerPassUtil; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares; +use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterAutowiredSubscribers; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterMessageRecorders; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterSubscribers; use SimpleBus\SymfonyBridge\DependencyInjection\EventBusExtension; @@ -48,6 +49,14 @@ public function build(ContainerBuilder $container) ) ); + $container->addCompilerPass( + new RegisterAutowiredSubscribers( + 'simple_bus.event_bus.event_subscribers_collection', + 'event_subscriber_autowired', + 'event_subscriber' + ) + ); + CompilerPassUtil::prependBeforeOptimizationPass( $container, new AddMiddlewareTags( diff --git a/tests/Functional/AutowiringTest.php b/tests/Functional/AutowiringTest.php new file mode 100644 index 0000000..ba12bb1 --- /dev/null +++ b/tests/Functional/AutowiringTest.php @@ -0,0 +1,33 @@ +boot(); + $container = $kernel->getContainer(); + + $eventBus = $container->get('event_bus'); + $eventBus->handle(new UserRegistered(42)); + $eventBus->handle(new UserChangedEmail(42)); + + $subscriber = $container->get('test_event_subscriber'); + /** @var $subscriber SendEmailVerificationInstructionsSubscriber */ + + $this->assertTrue($subscriber->userRegisteredHandled); + $this->assertTrue($subscriber->userChangedEmailHandled); + } +} diff --git a/tests/Functional/AutowiringTest/SendEmailVerificationInstructionsSubscriber.php b/tests/Functional/AutowiringTest/SendEmailVerificationInstructionsSubscriber.php new file mode 100644 index 0000000..d76cbfb --- /dev/null +++ b/tests/Functional/AutowiringTest/SendEmailVerificationInstructionsSubscriber.php @@ -0,0 +1,22 @@ +userRegisteredHandled = true; + } + + public function onUserChangedEmail(UserChangedEmail $event) + { + $this->userChangedEmailHandled = true; + } +} diff --git a/tests/Functional/AutowiringTest/UserChangedEmail.php b/tests/Functional/AutowiringTest/UserChangedEmail.php new file mode 100644 index 0000000..dedf01b --- /dev/null +++ b/tests/Functional/AutowiringTest/UserChangedEmail.php @@ -0,0 +1,19 @@ +userId = $userId; + } +} diff --git a/tests/Functional/AutowiringTest/UserRegistered.php b/tests/Functional/AutowiringTest/UserRegistered.php new file mode 100644 index 0000000..5b5511f --- /dev/null +++ b/tests/Functional/AutowiringTest/UserRegistered.php @@ -0,0 +1,19 @@ +userId = $userId; + } +} diff --git a/tests/Functional/AutowiringTest/config.yml b/tests/Functional/AutowiringTest/config.yml new file mode 100644 index 0000000..6296a24 --- /dev/null +++ b/tests/Functional/AutowiringTest/config.yml @@ -0,0 +1,18 @@ +services: + test_event_subscriber: + class: SimpleBus\SymfonyBridge\Tests\Functional\AutowiringTest\SendEmailVerificationInstructionsSubscriber + tags: + - { name: event_subscriber_autowired } + +event_bus: + event_name_resolver_strategy: class_based + +doctrine: + dbal: + driver: pdo_sqlite + path: :memory: + memory: true + orm: + entity_managers: + default: + connection: default diff --git a/tests/Functional/SmokeTest.php b/tests/Functional/SmokeTest.php index 0d0cf95..aa1ec47 100644 --- a/tests/Functional/SmokeTest.php +++ b/tests/Functional/SmokeTest.php @@ -5,7 +5,7 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\SchemaTool; use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand; -use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestKernel; +use SimpleBus\SymfonyBridge\Tests\Functional\TestKernel; use Symfony\Component\DependencyInjection\ContainerInterface; class SmokeTest extends \PHPUnit_Framework_TestCase @@ -15,7 +15,7 @@ class SmokeTest extends \PHPUnit_Framework_TestCase */ public function it_handles_a_command_then_dispatches_events_for_all_modified_entities() { - $kernel = new TestKernel('test', true); + $kernel = new TestKernel('test', true, 'smoke', __DIR__ . '/SmokeTest/config.yml'); $kernel->boot(); $container = $kernel->getContainer(); diff --git a/tests/Functional/SmokeTest/config.yml b/tests/Functional/SmokeTest/config.yml index fd1b3c9..428d90d 100644 --- a/tests/Functional/SmokeTest/config.yml +++ b/tests/Functional/SmokeTest/config.yml @@ -30,8 +30,6 @@ services: class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherEventSubscriber tags: - { name: event_subscriber, subscribes_to: some_other_event } - arguments: - - '@command_bus' doctrine: dbal: @@ -45,7 +43,7 @@ doctrine: mappings: test: type: annotation - dir: "%kernel.root_dir%/Entity/" + dir: "%kernel.root_dir%/SmokeTest/Entity/" prefix: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Entity alias: Test is_bundle: false diff --git a/tests/Functional/SmokeTest/TestKernel.php b/tests/Functional/TestKernel.php similarity index 81% rename from tests/Functional/SmokeTest/TestKernel.php rename to tests/Functional/TestKernel.php index f48e2c9..be5eed7 100644 --- a/tests/Functional/SmokeTest/TestKernel.php +++ b/tests/Functional/TestKernel.php @@ -1,6 +1,6 @@ tempDir = sys_get_temp_dir() . '/' . uniqid(); mkdir($this->tempDir, 0777, true); + + $this->name = $name; + $this->configPath = $configPath; } public function registerBundles() @@ -35,7 +39,7 @@ public function registerBundles() public function registerContainerConfiguration(LoaderInterface $loader) { - $loader->load(__DIR__ . '/config.yml'); + $loader->load($this->configPath); } public function getCacheDir()