├── Changelog.md ├── Message ├── HandlesMessagesAsync.php ├── DelayedMessage.php ├── AutoRegisteredEventSubscriber.php ├── BaseEventSubscriber.php ├── Event │ └── Pong.php ├── Command │ └── Ping.php ├── CommandHandler │ └── PingHandler.php ├── EventSubscriber │ └── WhenPongWriteLog.php ├── BaseCommandHandler.php └── Publisher │ ├── DirectPublisher.php │ └── RabbitMQPublisher.php ├── Resources └── config │ └── ping.yml ├── HappyrSimpleBusBundle.php ├── Command └── PingCommand.php ├── composer.json ├── DependencyInjection ├── Configuration.php ├── CompilerPass │ └── CompilerPasses.php └── HappyrSimpleBusExtension.php └── Readme.md /Changelog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release. 4 | 5 | ## 0.1.0 6 | 7 | First release 8 | -------------------------------------------------------------------------------- /Message/HandlesMessagesAsync.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | interface AutoRegisteredEventSubscriber 9 | { 10 | /** 11 | * A namespace + class for the event the current subscriber is listening to. 12 | * 13 | * @return string Example: FooEvent:class 14 | */ 15 | public static function subscribesTo(); 16 | } 17 | -------------------------------------------------------------------------------- /Message/BaseEventSubscriber.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | abstract class BaseEventSubscriber implements AutoRegisteredEventSubscriber 11 | { 12 | /** 13 | * @var EntityManagerInterface em 14 | */ 15 | protected $em; 16 | 17 | /** 18 | * @param EntityManagerInterface $em 19 | */ 20 | public function setEntityManager(EntityManagerInterface $em) 21 | { 22 | $this->em = $em; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Resources/config/ping.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | services: 4 | rawls.command_handler.PingHandler: 5 | class: Happyr\SimpleBusBundle\Message\CommandHandler\PingHandler 6 | arguments: ["@?logger"] 7 | tags: 8 | - { name: asynchronous_command_handler, handles: Happyr\SimpleBusBundle\Message\Command\Ping } 9 | 10 | rawls.event_subscriber.WhenPongWriteLog: 11 | class: Happyr\SimpleBusBundle\Message\EventSubscriber\WhenPongWriteLog 12 | arguments: ["@?logger"] 13 | tags: 14 | - { name: asynchronous_event_subscriber, subscribes_to: Happyr\SimpleBusBundle\Message\Event\Pong } 15 | -------------------------------------------------------------------------------- /HappyrSimpleBusBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new CompilerPasses()); 15 | } 16 | 17 | public function getContainerExtension() 18 | { 19 | return new HappyrSimpleBusExtension(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Message/Event/Pong.php: -------------------------------------------------------------------------------- 1 | data = $data; 22 | } 23 | 24 | /** 25 | * @return mixed 26 | */ 27 | public function getData() 28 | { 29 | return $this->data; 30 | } 31 | 32 | public function getDelayedTime() 33 | { 34 | return 5000; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Message/Command/Ping.php: -------------------------------------------------------------------------------- 1 | data = $data; 22 | } 23 | 24 | /** 25 | * @return mixed 26 | */ 27 | public function getData() 28 | { 29 | return $this->data; 30 | } 31 | 32 | public function getDelayedTime() 33 | { 34 | return 2000; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Message/CommandHandler/PingHandler.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 21 | } 22 | 23 | public function handle(Ping $command) 24 | { 25 | $data = $command->getData(); 26 | 27 | if (null !== $this->logger) { 28 | $this->logger->error('Ping command handler works!', ['data' => $data]); 29 | } 30 | 31 | return; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Message/EventSubscriber/WhenPongWriteLog.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 21 | } 22 | 23 | public function notify(Pong $event) 24 | { 25 | $data = $event->getData(); 26 | 27 | if (null !== $this->logger) { 28 | $this->logger->error('Pong event subscriber works!', ['data' => $data]); 29 | } 30 | 31 | return; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Message/BaseCommandHandler.php: -------------------------------------------------------------------------------- 1 | eventRecorder = $eventRecorder; 29 | } 30 | 31 | /** 32 | * @param EntityManagerInterface $em 33 | */ 34 | public function setEntityManager(EntityManagerInterface $em) 35 | { 36 | $this->em = $em; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Command/PingCommand.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class PingCommand extends ContainerAwareCommand 17 | { 18 | protected function configure() 19 | { 20 | $this 21 | ->setName('happyr:simplebus:ping') 22 | ->setDescription('Ping test'); 23 | } 24 | 25 | protected function execute(InputInterface $input, OutputInterface $output) 26 | { 27 | $this->get('command_bus')->handle(new Ping('4711')); 28 | $this->get('event_bus')->handle(new Pong('4711-pong')); 29 | } 30 | 31 | /** 32 | * @param string $service 33 | * 34 | * @return object 35 | */ 36 | private function get(string $service) 37 | { 38 | return $this->getContainer()->get($service); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "happyr/simplebus-bundle", 3 | "type": "symfony-bundle", 4 | "description": "How Happyr use SimpleBus", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Tobias Nyholm", 9 | "email": "tobias.nyholm@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^7.0", 14 | "psr/log": "~1.0", 15 | 16 | "happyr/mq2php-bundle": "^0.2.4", 17 | "simple-bus/symfony-bridge": "^4.1.5", 18 | "simple-bus/doctrine-orm-bridge": "^4.0", 19 | "simple-bus/jms-serializer-bundle-bridge": "^2.0.2", 20 | "simple-bus/rabbitmq-bundle-bridge": "^3.1", 21 | "simple-bus/asynchronous-bundle": "^2.3", 22 | "simple-bus/asynchronous": "^2.1" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "^5.7", 26 | "symfony/symfony": "^3.0", 27 | "simple-bus/jms-serializer-bundle-bridge": "^2.0", 28 | "nyholm/symfony-bundle-test": "^1.0.2" 29 | }, 30 | "autoload": { 31 | "psr-4": { "Happyr\\SimpleBusBundle\\": "" } 32 | }, 33 | "scripts": { 34 | "test": "vendor/bin/phpunit", 35 | "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('happyr_simplebus'); 20 | 21 | $root->children() 22 | ->arrayNode('auto_register_handlers')->addDefaultsIfNotSet()->children() 23 | ->booleanNode('enabled')->defaultTrue()->end() 24 | ->scalarNode('command_namespace')->defaultNull()->end() 25 | ->scalarNode('command_handler_namespace')->defaultNull()->end() 26 | ->scalarNode('command_handler_path')->defaultNull()->end() 27 | ->end()->end() 28 | ->arrayNode('auto_register_event_subscribers')->addDefaultsIfNotSet()->children() 29 | ->booleanNode('enabled')->defaultTrue()->end() 30 | ->scalarNode('event_subscriber_namespace')->defaultNull()->end() 31 | ->scalarNode('event_subscriber_path')->defaultNull()->end() 32 | ->end()->end() 33 | ->booleanNode('use_direct_publisher')->defaultFalse()->end() 34 | ->end(); 35 | 36 | return $treeBuilder; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Message/Publisher/DirectPublisher.php: -------------------------------------------------------------------------------- 1 | consumer = $consumer; 37 | $this->serializer = $serializer; 38 | $this->queueName = $queueName; 39 | } 40 | 41 | public function publish($message) 42 | { 43 | if ($message instanceof DelayedMessage) { 44 | $this->delayedMessages[$message->getDelayedTime()][] = $this->serializer->wrapAndSerialize($message); 45 | } else { 46 | $this->messages[] = $this->serializer->wrapAndSerialize($message); 47 | } 48 | } 49 | 50 | public function consume() 51 | { 52 | foreach ($this->messages as $message) { 53 | $this->doConsume($message); 54 | } 55 | 56 | ksort($this->delayedMessages); 57 | foreach ($this->delayedMessages as $messages) { 58 | foreach ($messages as $message) { 59 | $this->doConsume($message); 60 | } 61 | } 62 | } 63 | 64 | public function __destruct() 65 | { 66 | $this->consume(); 67 | } 68 | 69 | /** 70 | * @param string $data 71 | */ 72 | private function doConsume(string $data) 73 | { 74 | $message = json_decode($data, true); 75 | $body = $message['body']; 76 | 77 | $this->consumer->consume($this->queueName, $body); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Message/Publisher/RabbitMQPublisher.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class RabbitMQPublisher implements Publisher 19 | { 20 | /** 21 | * @var MessageInEnvelopSerializer 22 | */ 23 | private $serializer; 24 | 25 | /** 26 | * @var Producer|Fallback 27 | */ 28 | private $producer; 29 | 30 | /** 31 | * @var RoutingKeyResolver 32 | */ 33 | private $routingKeyResolver; 34 | 35 | /** 36 | * @var AdditionalPropertiesResolver 37 | */ 38 | private $additionalPropertiesResolver; 39 | 40 | public function __construct( 41 | MessageInEnvelopSerializer $messageSerializer, 42 | $producer, 43 | RoutingKeyResolver $routingKeyResolver, 44 | AdditionalPropertiesResolver $additionalPropertiesResolver 45 | ) { 46 | if (!$producer instanceof Producer && !$producer instanceof Fallback) { 47 | throw new \LogicException('Producer must be an instance of OldSound\RabbitMqBundle\RabbitMq\Producer or OldSound\RabbitMqBundle\RabbitMq\Fallback'); 48 | } 49 | 50 | $this->serializer = $messageSerializer; 51 | $this->producer = $producer; 52 | $this->routingKeyResolver = $routingKeyResolver; 53 | $this->additionalPropertiesResolver = $additionalPropertiesResolver; 54 | } 55 | 56 | /** 57 | * Publish the given Message by serializing it and handing it over to a RabbitMQ producer. 58 | * 59 | * {@inheritdoc} 60 | */ 61 | public function publish($message) 62 | { 63 | $serializedMessage = $this->serializer->wrapAndSerialize($message); 64 | $routingKey = $this->routingKeyResolver->resolveRoutingKeyFor($message); 65 | $additionalProperties = $this->additionalPropertiesResolver->resolveAdditionalPropertiesFor($message); 66 | 67 | $headers = []; 68 | if ($message instanceof DelayedMessage) { 69 | $headers['x-delay'] = $message->getDelayedTime(); 70 | } 71 | 72 | $this->producer->publish($serializedMessage, $routingKey, $additionalProperties, $headers); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # SimpleBusBundle 2 | 3 | [![Latest Version](https://img.shields.io/github/release/Happyr/SimpleBusBundle.svg?style=flat-square)](https://github.com/Happyr/SimpleBusBundle/releases) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Build Status](https://img.shields.io/travis/Happyr/SimpleBusBundle.svg?style=flat-square)](https://travis-ci.org/Happyr/SimpleBusBundle) 6 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Happyr/SimpleBusBundle.svg?style=flat-square)](https://scrutinizer-ci.com/g/Happyr/SimpleBusBundle) 7 | [![Quality Score](https://img.shields.io/scrutinizer/g/Happyr/SimpleBusBundle.svg?style=flat-square)](https://scrutinizer-ci.com/g/Happyr/SimpleBusBundle) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/happyr/simplebus-bundle.svg?style=flat-square)](https://packagist.org/packages/happyr/simplebus-bundle) 9 | 10 | 11 | This bundle includes all the nice extra features Happyr needs for their SimpleBus installation. The purpose is not to be 12 | 100% resuable and flexible. Feel free to for it and adjust it for your needs. 13 | 14 | ### Installation 15 | 16 | ``` 17 | composer require happyr/simplebus-bundle 18 | ``` 19 | 20 | ```php 21 | 22 | class AppKernel extends Kernel 23 | { 24 | public function registerBundles() 25 | { 26 | $bundles = [ 27 | // ... 28 | new Happyr\SimpleBusBundle\HappyrSimpleBusBundle(), // <-- Make sure this is before the SimpleBusBrige bundles. 29 | new Happyr\Mq2phpBundle\HappyrMq2phpBundle(), 30 | new SimpleBus\SymfonyBridge\SimpleBusCommandBusBundle(), 31 | new SimpleBus\SymfonyBridge\SimpleBusEventBusBundle(), 32 | new SimpleBus\AsynchronousBundle\SimpleBusAsynchronousBundle(), 33 | new SimpleBus\RabbitMQBundleBridge\SimpleBusRabbitMQBundleBridgeBundle(), 34 | new SimpleBus\JMSSerializerBundleBridge\SimpleBusJMSSerializerBundleBridgeBundle(), 35 | new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(), 36 | new JMS\SerializerBundle\JMSSerializerBundle(), 37 | new SimpleBus\JMSSerializerBundleBridge\SimpleBusJMSSerializerBundleBridgeBundle(), 38 | ]; 39 | // ... 40 | } 41 | // ... 42 | } 43 | ``` 44 | 45 | ```yaml 46 | # /app/config/happyr_simplebus.yml 47 | 48 | parameters: 49 | app.command_queue: 'commands' 50 | app.event_queue: 'events' 51 | simple_bus.command_bus.logging.level: info 52 | simple_bus.event_bus.logging.level: info 53 | 54 | happyr_mq2php: 55 | enabled: true 56 | secret_key: 'CHANGE_ME' 57 | command_queue: "%app.command_queue%" 58 | event_queue: "%app.event_queue%" 59 | message_headers: 60 | fastcgi_host: "%fastcgi_host%" 61 | fastcgi_port: "%fastcgi_port%" 62 | dispatch_path: "%mq2php_dispatch_path%" 63 | 64 | command_bus: 65 | logging: ~ 66 | 67 | event_bus: 68 | logging: ~ 69 | 70 | simple_bus_rabbit_mq_bundle_bridge: 71 | commands: 72 | # this producer service will be defined by OldSoundRabbitMqBundle, 73 | # its name is old_sound_rabbit_mq.%producer_name%_producer 74 | producer_service_id: old_sound_rabbit_mq.asynchronous_commands_producer 75 | events: 76 | # this producer service will be defined by OldSoundRabbitMqBundle, 77 | # its name is old_sound_rabbit_mq.%producer_name%_producer 78 | producer_service_id: old_sound_rabbit_mq.asynchronous_events_producer 79 | 80 | simple_bus_asynchronous: 81 | events: 82 | strategy: 'predefined' 83 | 84 | old_sound_rabbit_mq: 85 | connections: 86 | default: 87 | host: "%rabbitmq_host%" 88 | port: 5672 89 | user: 'guest' 90 | password: 'guest' 91 | vhost: '/' 92 | lazy: false 93 | connection_timeout: 3 94 | read_write_timeout: 3 95 | 96 | # requires php-amqplib v2.4.1+ and PHP5.4+ 97 | keepalive: false 98 | 99 | # requires php-amqplib v2.4.1+ 100 | heartbeat: 0 101 | producers: 102 | asynchronous_commands: 103 | connection: default 104 | exchange_options: { name: '%app.command_queue%', type: "x-delayed-message", arguments: {"x-delayed-type": ["S","direct"]} } 105 | queue_options: { name: "%app.command_queue%", durable: true } 106 | 107 | asynchronous_events: 108 | connection: default 109 | exchange_options: { name: '%app.event_queue%', type: "x-delayed-message", arguments: {"x-delayed-type": ["S","direct"]} } 110 | queue_options: { name: "%app.event_queue%", durable: true } 111 | ``` 112 | 113 | Continue to read at [Mq2phpBundle](https://github.com/Happyr/Mq2phpBundle). Make sure to install the [RabbitMQ extension](https://github.com/rabbitmq/rabbitmq-delayed-message-exchange) 114 | for delayed messages. 115 | 116 | ### Use 117 | 118 | Create your messages in 119 | ``` 120 | src/App/Message 121 | Command 122 | CommandHandler 123 | Event 124 | EventSubscriber 125 | ``` 126 | And they will be auto wired and registered automatically. You may of course register them manually. 127 | 128 | #### Classes & Interfaces 129 | 130 | Be aware of the following base classes. 131 | 132 | * `BaseCommandHandler` 133 | * `BaseEventSubscriber` implements `AutoRegisteredEventSubscriber` 134 | * `HandlesMessagesAsync` (For async handlers/subscribers) 135 | * `DelayedMessage` (For async messages with a delay) 136 | 137 | ### Direct publisher 138 | 139 | If do not want to use a queue you may use the direct publisher. 140 | 141 | ```php 142 | happyr_simplebus: 143 | use_direct_publisher: true 144 | 145 | ``` 146 | 147 | -------------------------------------------------------------------------------- /DependencyInjection/CompilerPass/CompilerPasses.php: -------------------------------------------------------------------------------- 1 | removeCommandHandlerDuplicates($container); 20 | $this->handleEventSubscriberDuplicates($container); 21 | $this->processMessageHandlers($container); 22 | $this->replaceSimpleBusPublisher($container); 23 | } 24 | 25 | /** 26 | * If we find to command handler services that are registered on the same command, make sure we remove the one with '.auto' on the end. 27 | * 28 | * @param ContainerBuilder $container 29 | */ 30 | private function removeCommandHandlerDuplicates(ContainerBuilder $container) 31 | { 32 | $taggedServices = array_merge($container->findTaggedServiceIds('command_handler'), $container->findTaggedServiceIds('asynchronous_command_handler')); 33 | 34 | $commands = []; 35 | 36 | foreach ($taggedServices as $id => $tags) { 37 | foreach ($tags as $tag) { 38 | if (isset($commands[$tag['handles']])) { 39 | // Find the one that ends with '.auto' 40 | $removeServiceId = null; 41 | 42 | if ('.auto' === substr($id, -5)) { 43 | $removeServiceId = $id; 44 | } 45 | 46 | if ('.auto' === substr($commands[$tag['handles']], -5)) { 47 | $removeServiceId = $commands[$tag['handles']]; 48 | $commands[$tag['handles']] = $id; 49 | } 50 | 51 | if (null !== $removeServiceId) { 52 | // Remove the definition 53 | $container->removeDefinition($removeServiceId); 54 | 55 | continue; 56 | } 57 | } 58 | $commands[$tag['handles']] = $id; 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * @param ContainerBuilder $container 65 | */ 66 | private function handleEventSubscriberDuplicates(ContainerBuilder $container) 67 | { 68 | $taggedServices = array_merge($container->findTaggedServiceIds('event_subscriber'), $container->findTaggedServiceIds('asynchronous_event_subscriber')); 69 | 70 | // Keys are event class names 71 | $events = []; 72 | 73 | foreach ($taggedServices as $id => $tags) { 74 | foreach ($tags as $tag) { 75 | if (isset($tag['subscribes_to'])) { 76 | $events[$tag['subscribes_to']][] = $id; 77 | } 78 | } 79 | } 80 | 81 | foreach ($events as $eventClass => $subscribersIds) { 82 | if (count($subscribersIds) <= 1) { 83 | continue; 84 | } 85 | 86 | // Get services 87 | $subscriberClassNames = []; 88 | foreach ($subscribersIds as $subscribersId) { 89 | $service = $container->getDefinition($subscribersId); 90 | $subscriberClassNames[$service->getClass()][] = $subscribersId; 91 | } 92 | 93 | foreach ($subscriberClassNames as $className => $services) { 94 | if (count($services) <= 1) { 95 | continue; 96 | } 97 | 98 | // IF we have multiple services registed to the same event subscriber, remove the auto added ones. 99 | foreach ($services as $serviceId) { 100 | if ('.auto' === substr($serviceId, -5)) { 101 | $container->removeDefinition($serviceId); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * @param ContainerBuilder $container 110 | */ 111 | private function processMessageHandlers(ContainerBuilder $container) 112 | { 113 | $taggedServices = array_merge( 114 | $container->findTaggedServiceIds('command_handler'), 115 | $container->findTaggedServiceIds('event_subscriber'), 116 | $container->findTaggedServiceIds('asynchronous_command_handler'), 117 | $container->findTaggedServiceIds('asynchronous_event_subscriber') 118 | ); 119 | $doctrine = $this->hasDoctrine($container); 120 | 121 | foreach ($taggedServices as $id => $tags) { 122 | $def = $container->findDefinition($id); 123 | $class = $def->getClass(); 124 | 125 | if (method_exists($class, 'setEventRecorder')) { 126 | $def->addMethodCall('setEventRecorder', [new Reference('event_recorder')]); 127 | } 128 | 129 | if (method_exists($class, 'setCommandBus')) { 130 | $def->addMethodCall('setCommandBus', [new Reference('command_bus')]); 131 | } 132 | 133 | if (method_exists($class, 'setEventBus')) { 134 | $def->addMethodCall('setEventBus', [new Reference('event_bus')]); 135 | } 136 | 137 | if ($doctrine && method_exists($class, 'setEntityManager')) { 138 | $def->addMethodCall('setEntityManager', [new Reference('doctrine.orm.entity_manager')]); 139 | } 140 | 141 | if (in_array('Psr\Log\LoggerAwareInterface', class_implements($class))) { 142 | $def->addMethodCall('setLogger', [new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE)]); 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * @param ContainerBuilder $container 149 | */ 150 | private function replaceSimpleBusPublisher(ContainerBuilder $container) 151 | { 152 | if (!$container->has('simple_bus.rabbit_mq_bundle_bridge.event_publisher')) { 153 | return; 154 | } 155 | 156 | if (!$container->getParameter('happyr.simplebus.direct_publisher')) { 157 | $container->getDefinition('simple_bus.rabbit_mq_bundle_bridge.event_publisher') 158 | ->setClass(RabbitMQPublisher::class) 159 | ->setLazy(true); 160 | $container->getDefinition('simple_bus.rabbit_mq_bundle_bridge.command_publisher') 161 | ->setClass(RabbitMQPublisher::class) 162 | ->setLazy(true); 163 | } else { 164 | $container->getDefinition('simple_bus.rabbit_mq_bundle_bridge.event_publisher') 165 | ->setClass(DirectPublisher::class) 166 | ->setLazy(true) 167 | ->setArguments([ 168 | new Reference('happyr.mq2php.consumer_wrapper'), 169 | new Reference('happyr.mq2php.message_serializer'), 170 | $container->getParameter('happyr.mq2php.event_queue_name'), 171 | ]); 172 | 173 | $container->getDefinition('simple_bus.rabbit_mq_bundle_bridge.command_publisher') 174 | ->setClass(DirectPublisher::class) 175 | ->setLazy(true) 176 | ->setArguments([ 177 | new Reference('happyr.mq2php.consumer_wrapper'), 178 | new Reference('happyr.mq2php.message_serializer'), 179 | $container->getParameter('happyr.mq2php.command_queue_name'), 180 | ]); 181 | } 182 | } 183 | 184 | /** 185 | * @param ContainerBuilder $container 186 | * 187 | * @return bool 188 | */ 189 | private function hasDoctrine(ContainerBuilder $container) 190 | { 191 | return $container->hasDefinition('doctrine.orm.entity_manager') || 192 | $container->hasAlias('doctrine.orm.entity_manager'); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /DependencyInjection/HappyrSimpleBusExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 28 | 29 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 30 | $loader->load('ping.yml'); 31 | $this->requireBundle('SimpleBusCommandBusBundle', $container); 32 | $this->requireBundle('SimpleBusEventBusBundle', $container); 33 | $this->requireBundle('SimpleBusAsynchronousBundle', $container); 34 | $this->requireBundle('SimpleBusRabbitMQBundleBridgeBundle', $container); 35 | $this->requireBundle('SimpleBusJMSSerializerBundleBridgeBundle', $container); 36 | $this->requireBundle('HappyrMq2phpBundle', $container); 37 | $this->requireBundle('OldSoundRabbitMqBundle', $container); 38 | $this->requireBundle('JMSSerializerBundle', $container); 39 | 40 | $container->setParameter('happyr.simplebus.direct_publisher', $config['use_direct_publisher']); 41 | if ($config['auto_register_handlers']['enabled']) { 42 | $handlerPath = $config['auto_register_handlers']['command_handler_path']; 43 | if (empty($handlerPath)) { 44 | $rootDir = $container->getParameter('kernel.root_dir'); 45 | $handlerPath = $rootDir.'/../src/App/Message/CommandHandler'; 46 | } 47 | 48 | $commandNamespace = $config['auto_register_handlers']['command_namespace']; 49 | if (empty($commandNamespace)) { 50 | $commandNamespace = 'App\\Message\\Command'; 51 | } 52 | 53 | $handlerNamespace = $config['auto_register_handlers']['command_handler_namespace']; 54 | if (empty($handlerNamespace)) { 55 | $handlerNamespace = 'App\\Message\\CommandHandler'; 56 | } 57 | 58 | $this->autoRegisterCommands($container, $commandNamespace, $handlerNamespace, $handlerPath); 59 | } 60 | 61 | if ($config['auto_register_event_subscribers']['enabled']) { 62 | $path = $config['auto_register_event_subscribers']['event_subscriber_path']; 63 | if (empty($path)) { 64 | $rootDir = $container->getParameter('kernel.root_dir'); 65 | $path = $rootDir.'/../src/App/Message/EventSubscriber'; 66 | } 67 | 68 | $namespace = $config['auto_register_event_subscribers']['event_subscriber_namespace']; 69 | if (empty($namespace)) { 70 | $namespace = 'App\\Message\\EventSubscriber'; 71 | } 72 | 73 | $this->autoRegisterEventSubscribers($container, $namespace, $path); 74 | } 75 | } 76 | 77 | /** 78 | * @param ContainerBuilder $container 79 | * @param string $commandNamespace 80 | * @param string $handlerNamespace 81 | * @param string $handlerPath 82 | */ 83 | protected function autoRegisterCommands(ContainerBuilder $container, $commandNamespace, $handlerNamespace, $handlerPath) 84 | { 85 | // Make sure it ends with slash 86 | $commandNamespace = rtrim($commandNamespace, '\\').'\\'; 87 | $handlerNamespace = rtrim($handlerNamespace, '\\').'\\'; 88 | 89 | $finder = new Finder(); 90 | 91 | try { 92 | $finder->files()->in($handlerPath)->name('*Handler.php'); 93 | } catch (\InvalidArgumentException $e) { 94 | return; 95 | } 96 | 97 | foreach ($finder as $file) { 98 | $handlerClassName = $file->getBasename('.php'); 99 | $commandClassName = $file->getBasename('Handler.php'); 100 | 101 | $dynamicContainerId = ''; 102 | $dynamicFQN = ''; 103 | $path = $file->getRelativePath(); 104 | if (!empty($path)) { 105 | $dynamicFQN = str_replace('/', '\\', $path).'\\'; 106 | $dynamicContainerId = str_replace('/', '.', $path).'.'; 107 | } 108 | 109 | $commandFQN = $commandNamespace.$dynamicFQN.$commandClassName; 110 | $handlerFQN = $handlerNamespace.$dynamicFQN.$handlerClassName; 111 | 112 | $containerId = strtolower(sprintf('command_handler.%s%s.auto', $dynamicContainerId, ltrim(preg_replace('/[A-Z]/', '_$0', $commandClassName), '_'))); 113 | 114 | $def = new Definition($handlerFQN); 115 | $def->setAutowired(true); 116 | 117 | $tag = 'command_handler'; 118 | if (is_subclass_of($handlerFQN, HandlesMessagesAsync::class)) { 119 | $tag = 'asynchronous_command_handler'; 120 | } 121 | 122 | $def->addTag($tag, ['handles' => $commandFQN]); 123 | $container->setDefinition($containerId, $def); 124 | } 125 | } 126 | 127 | /** 128 | * @param ContainerBuilder $container 129 | * @param string $subscriberNamespace 130 | * @param string $subscriberPath 131 | */ 132 | protected function autoRegisterEventSubscribers(ContainerBuilder $container, $subscriberNamespace, $subscriberPath) 133 | { 134 | // Make sure it ends with slash 135 | $subscriberNamespace = rtrim($subscriberNamespace, '\\').'\\'; 136 | 137 | $finder = new Finder(); 138 | 139 | try { 140 | $finder->files()->in($subscriberPath)->name('*.php'); 141 | } catch (\InvalidArgumentException $e) { 142 | return; 143 | } 144 | 145 | foreach ($finder as $file) { 146 | $subscriberClassName = $file->getBasename('.php'); 147 | 148 | $dynamicContainerId = ''; 149 | $dynamicFQN = ''; 150 | $path = $file->getRelativePath(); 151 | if (!empty($path)) { 152 | $dynamicFQN = str_replace('/', '\\', $path).'\\'; 153 | $dynamicContainerId = str_replace('/', '.', $path).'.'; 154 | } 155 | 156 | $subscriberFQN = $subscriberNamespace.$dynamicFQN.$subscriberClassName; 157 | if (!is_subclass_of($subscriberFQN, AutoRegisteredEventSubscriber::class)) { 158 | continue; 159 | } 160 | 161 | if (!method_exists($subscriberFQN, 'notify')) { 162 | throw new \LogicException(sprintf('An event subscriber that implements AutoRegisteredEventSubscriber must have a function called notify. Please add one in "%s"', $subscriberFQN)); 163 | } 164 | 165 | $containerId = strtolower(sprintf('event_subscriber.%s%s.auto', $dynamicContainerId, ltrim(preg_replace('/[A-Z]/', '_$0', $subscriberClassName), '_'))); 166 | $def = new Definition($subscriberFQN); 167 | $def->setAutowired(true); 168 | 169 | $tag = 'event_subscriber'; 170 | if (is_subclass_of($subscriberFQN, HandlesMessagesAsync::class)) { 171 | $tag = 'asynchronous_event_subscriber'; 172 | } 173 | 174 | $def->addTag($tag, ['subscribes_to' => call_user_func($subscriberFQN.'::subscribesTo')]); 175 | $container->setDefinition($containerId, $def); 176 | } 177 | } 178 | 179 | /** 180 | * Make sure we have activated the required bundles. 181 | * 182 | * @param $bundleName 183 | * @param ContainerBuilder $container 184 | */ 185 | private function requireBundle($bundleName, ContainerBuilder $container) 186 | { 187 | $enabledBundles = $container->getParameter('kernel.bundles'); 188 | if (!isset($enabledBundles[$bundleName])) { 189 | throw new \LogicException(sprintf('You need to enable "%s" as well', $bundleName)); 190 | } 191 | } 192 | 193 | public function getAlias() 194 | { 195 | return 'happyr_simplebus'; 196 | } 197 | } 198 | --------------------------------------------------------------------------------