├── src ├── Bus │ ├── EventBus.php │ └── CommandBus.php ├── Resources │ └── config │ │ ├── command_bus_logging.yml │ │ ├── event_bus_logging.yml │ │ ├── doctrine_orm_bridge.yml │ │ ├── command_bus.yml │ │ └── event_bus.yml ├── RequiresOtherBundles.php ├── DependencyInjection │ ├── Compiler │ │ ├── CollectServices.php │ │ ├── RegisterMessageRecorders.php │ │ ├── ConfigureMiddlewares.php │ │ ├── AddMiddlewareTags.php │ │ ├── RegisterHandlers.php │ │ ├── RegisterSubscribers.php │ │ └── AutoRegister.php │ ├── DoctrineOrmBridgeConfiguration.php │ ├── EventBusConfiguration.php │ ├── CommandBusConfiguration.php │ ├── DoctrineOrmBridgeExtension.php │ ├── EventBusExtension.php │ └── CommandBusExtension.php ├── SimpleBusCommandBusBundle.php ├── DoctrineOrmBridgeBundle.php └── SimpleBusEventBusBundle.php ├── couscous.yml ├── README.md ├── LICENSE └── composer.json /src/Bus/EventBus.php: -------------------------------------------------------------------------------- 1 | getParameter('kernel.bundles'); 21 | 22 | foreach ($requiredBundles as $requiredBundle) { 23 | if (!isset($enabledBundles[$requiredBundle])) { 24 | throw new LogicException(sprintf('In order to use bundle "%s" you also need to enable "%s"', $this->getName(), $requiredBundle)); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/CollectServices.php: -------------------------------------------------------------------------------- 1 | findTaggedServiceIds($tagName) as $serviceId => $tags) { 17 | foreach ($tags as $tagAttributes) { 18 | if (!isset($tagAttributes[$keyAttribute])) { 19 | throw new InvalidArgumentException(sprintf('The attribute "%s" of tag "%s" of service "%s" is mandatory', $keyAttribute, $tagName, $serviceId)); 20 | } 21 | 22 | $key = $tagAttributes[$keyAttribute]; 23 | 24 | call_user_func($callback, $key, $serviceId, $tagAttributes); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DependencyInjection/DoctrineOrmBridgeConfiguration.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 15 | } 16 | 17 | public function getConfigTreeBuilder(): TreeBuilder 18 | { 19 | $treeBuilder = new TreeBuilder($this->alias); 20 | $rootNode = $treeBuilder->getRootNode(); 21 | 22 | $rootNode 23 | ->addDefaultsIfNotSet() 24 | ->children() 25 | ->scalarNode('entity_manager') 26 | ->defaultValue('default') 27 | ->end() 28 | ->scalarNode('connection') 29 | ->defaultValue('default') 30 | ->end() 31 | ->end(); 32 | 33 | return $treeBuilder; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /couscous.yml: -------------------------------------------------------------------------------- 1 | title: SimpleBus/SymfonyBridge 2 | subTitle: Use SimpleBus/MessageBus in a Symfony application 3 | baseUrl: //simplebus.github.io/SymfonyBridge 4 | menu: 5 | items: 6 | home: 7 | itemId: home 8 | text: Home 9 | relativeUrl: "" 10 | getting_started: 11 | itemId: getting_started 12 | text: Getting started 13 | relativeUrl: doc/getting_started.html 14 | command_bus_bundle: 15 | itemId: command_bus_bundle 16 | text: Command bus bundle 17 | relativeUrl: doc/command_bus_bundle.html 18 | event_bus_bundle: 19 | itemId: event_bus_bundle 20 | text: Event bus bundle 21 | relativeUrl: doc/event_bus_bundle.html 22 | doctrine_orm_bridge_bundle: 23 | itemId: doctrine_orm_bridge_bundle 24 | text: Doctrine ORM bridge bundle 25 | relativeUrl: doc/doctrine_orm_bridge_bundle.html 26 | upgrade_guide: 27 | itemId: upgrade_guide 28 | text: Upgrade guide 29 | relativeUrl: doc/upgrade_guide.html 30 | -------------------------------------------------------------------------------- /src/DependencyInjection/EventBusConfiguration.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 15 | } 16 | 17 | public function getConfigTreeBuilder(): TreeBuilder 18 | { 19 | $treeBuilder = new TreeBuilder($this->alias); 20 | $rootNode = $treeBuilder->getRootNode(); 21 | 22 | $rootNode 23 | ->addDefaultsIfNotSet() 24 | ->children() 25 | ->enumNode('event_name_resolver_strategy') 26 | ->values(['class_based', 'named_message']) 27 | ->defaultValue('class_based') 28 | ->end() 29 | ->arrayNode('logging') 30 | ->canBeEnabled() 31 | ->end() 32 | ->end(); 33 | 34 | return $treeBuilder; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleBus/SymfonyBridge 2 | 3 | [![Tests Actions Status](https://github.com/SimpleBus/SimpleBus/workflows/Tests/badge.svg)](https://github.com/SimpleBus/SimpleBus/actions) 4 | 5 | By [Matthias Noback](http://php-and-symfony.matthiasnoback.nl/), Cliff Odijk, Ruud Kamphuis 6 | 7 | This package contains the following bundles which can be used to integrate 8 | [SimpleBus/MessageBus](https://github.com/SimpleBus/MessageBus) with a Symfony application: 9 | 10 | - [CommandBusBundle](http://simplebus.github.io/SymfonyBridge/doc/command_bus_bundle.html) 11 | - [EventBusBundle](http://simplebus.github.io/SymfonyBridge/doc/event_bus_bundle.html) 12 | - [DoctrineORMBridgeBundle](http://simplebus.github.io/SymfonyBridge/doc/doctrine_orm_bridge_bundle.html) 13 | 14 | Are you upgrading from a previous version? Read the [upgrade 15 | guide](http://simplebus.github.io/SymfonyBridge/doc/upgrade_guide.html). 16 | 17 | Resources 18 | --------- 19 | 20 | * [Report issues](https://github.com/SimpleBus/SimpleBus/issues) and 21 | [send Pull Requests](https://github.com/SimpleBus/SimpleBus/pulls) 22 | in the [main SimpleBus repository](https://github.com/SimpleBus/SimpleBus) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2018 Matthias Noback, Cliff Odijk, Ruud Kamphuis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RegisterMessageRecorders.php: -------------------------------------------------------------------------------- 1 | aggregatorId = $aggregatorId; 21 | $this->recorderTag = $recorderTag; 22 | } 23 | 24 | public function process(ContainerBuilder $container): void 25 | { 26 | if (!$container->has($this->aggregatorId)) { 27 | return; 28 | } 29 | 30 | $aggregator = $container->findDefinition($this->aggregatorId); 31 | 32 | $recorders = []; 33 | foreach (array_keys($container->findTaggedServiceIds($this->recorderTag)) as $recorderId) { 34 | $recorders[] = new Reference($recorderId); 35 | } 36 | 37 | $aggregator->replaceArgument(0, $recorders); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/DependencyInjection/CommandBusConfiguration.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 15 | } 16 | 17 | public function getConfigTreeBuilder(): TreeBuilder 18 | { 19 | $treeBuilder = new TreeBuilder($this->alias); 20 | $rootNode = $treeBuilder->getRootNode(); 21 | 22 | $rootNode 23 | ->addDefaultsIfNotSet() 24 | ->children() 25 | ->enumNode('command_name_resolver_strategy') 26 | ->values(['class_based', 'named_message']) 27 | ->defaultValue('class_based') 28 | ->end() 29 | ->arrayNode('middlewares') 30 | ->addDefaultsIfNotSet() 31 | ->children() 32 | ->booleanNode('logger')->defaultFalse()->end() 33 | ->booleanNode('finishes_command_before_handling_next')->defaultTrue()->end() 34 | ->end() 35 | ->end() 36 | ->arrayNode('logging') 37 | ->canBeEnabled() 38 | ->end() 39 | ->end(); 40 | 41 | return $treeBuilder; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/ConfigureMiddlewares.php: -------------------------------------------------------------------------------- 1 | mainBusId = $mainBusId; 18 | $this->busTag = $busTag; 19 | } 20 | 21 | public function process(ContainerBuilder $container): void 22 | { 23 | if (!$container->has($this->mainBusId)) { 24 | return; 25 | } 26 | 27 | $middlewareIds = new SplPriorityQueue(); 28 | 29 | foreach ($container->findTaggedServiceIds($this->busTag) as $specializedBusId => $tags) { 30 | foreach ($tags as $tagAttributes) { 31 | $priority = $tagAttributes['priority'] ?? 0; 32 | $middlewareIds->insert($specializedBusId, $priority); 33 | } 34 | } 35 | 36 | $orderedMiddlewareIds = iterator_to_array($middlewareIds, false); 37 | 38 | $mainBusDefinition = $container->findDefinition($this->mainBusId); 39 | foreach ($orderedMiddlewareIds as $middlewareId) { 40 | $mainBusDefinition->addMethodCall('appendMiddleware', [new Reference($middlewareId)]); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/SimpleBusCommandBusBundle.php: -------------------------------------------------------------------------------- 1 | configurationAlias = $alias; 20 | } 21 | 22 | public function build(ContainerBuilder $container): void 23 | { 24 | $container->addCompilerPass( 25 | new AutoRegister('command_handler', 'handles'), 26 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 27 | 10 28 | ); 29 | 30 | $container->addCompilerPass( 31 | new ConfigureMiddlewares( 32 | 'command_bus', 33 | 'command_bus_middleware' 34 | ) 35 | ); 36 | 37 | $container->addCompilerPass( 38 | new RegisterHandlers( 39 | 'simple_bus.command_bus.command_handler_map', 40 | 'simple_bus.command_bus.command_handler_service_locator', 41 | 'command_handler', 42 | 'handles' 43 | ) 44 | ); 45 | } 46 | 47 | public function getContainerExtension(): CommandBusExtension 48 | { 49 | return new CommandBusExtension($this->configurationAlias); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DependencyInjection/DoctrineOrmBridgeExtension.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 17 | } 18 | 19 | public function getAlias(): string 20 | { 21 | return $this->alias; 22 | } 23 | 24 | /** 25 | * @param mixed[] $config 26 | */ 27 | public function getConfiguration(array $config, ContainerBuilder $container): DoctrineOrmBridgeConfiguration 28 | { 29 | return new DoctrineOrmBridgeConfiguration($this->getAlias()); 30 | } 31 | 32 | /** 33 | * @param mixed[] $mergedConfig 34 | */ 35 | public function loadInternal(array $mergedConfig, ContainerBuilder $container): void 36 | { 37 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 38 | 39 | $loader->load('doctrine_orm_bridge.yml'); 40 | 41 | $container->setParameter( 42 | 'simple_bus.doctrine_orm_bridge.entity_manager', 43 | $mergedConfig['entity_manager'] 44 | ); 45 | 46 | $connection = $container->getParameterBag()->resolveValue($mergedConfig['connection']); 47 | $container 48 | ->findDefinition('simple_bus.doctrine_orm_bridge.collects_events_from_entities') 49 | ->addTag('doctrine.event_subscriber', ['connection' => $connection]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DoctrineOrmBridgeBundle.php: -------------------------------------------------------------------------------- 1 | configurationAlias = $configurationAlias; 21 | } 22 | 23 | public function getContainerExtension(): DoctrineOrmBridgeExtension 24 | { 25 | return new DoctrineOrmBridgeExtension($this->configurationAlias); 26 | } 27 | 28 | public function build(ContainerBuilder $container): void 29 | { 30 | $this->checkRequirements(['SimpleBusCommandBusBundle', 'SimpleBusEventBusBundle'], $container); 31 | 32 | $this->checkProxyManagerBridgeIsPresent(); 33 | 34 | $container->addCompilerPass( 35 | new AddMiddlewareTags( 36 | 'simple_bus.doctrine_orm_bridge.wraps_next_command_in_transaction', 37 | ['command'], 38 | 100 39 | ), 40 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 41 | 150 42 | ); 43 | } 44 | 45 | private function checkProxyManagerBridgeIsPresent(): void 46 | { 47 | if (!class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { 48 | throw new LogicException(sprintf('In order to use bundle "%s" you need to require "%s" package.', $this->getName(), 'symfony/proxy-manager-bridge')); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DependencyInjection/EventBusExtension.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 18 | } 19 | 20 | public function getAlias(): string 21 | { 22 | return $this->alias; 23 | } 24 | 25 | /** 26 | * @param mixed[] $config 27 | */ 28 | public function getConfiguration(array $config, ContainerBuilder $container): EventBusConfiguration 29 | { 30 | return new EventBusConfiguration($this->getAlias()); 31 | } 32 | 33 | /** 34 | * @param mixed[] $mergedConfig 35 | */ 36 | protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void 37 | { 38 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 39 | 40 | $loader->load('event_bus.yml'); 41 | 42 | $container->setAlias( 43 | 'simple_bus.event_bus.event_name_resolver', 44 | 'simple_bus.event_bus.'.$mergedConfig['event_name_resolver_strategy'].'_event_name_resolver' 45 | ); 46 | 47 | if ($mergedConfig['logging']['enabled']) { 48 | $loader->load('event_bus_logging.yml'); 49 | 50 | $container->getDefinition('simple_bus.event_bus.notifies_message_subscribers_middleware') 51 | ->replaceArgument(1, new Reference('logger')) 52 | ->replaceArgument(2, '%simple_bus.event_bus.logging.level%') 53 | ->addTag('monolog.logger', ['channel' => 'event_bus']); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DependencyInjection/CommandBusExtension.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 17 | } 18 | 19 | public function getAlias(): string 20 | { 21 | return $this->alias; 22 | } 23 | 24 | /** 25 | * @param mixed[] $config 26 | */ 27 | public function getConfiguration(array $config, ContainerBuilder $container): CommandBusConfiguration 28 | { 29 | return new CommandBusConfiguration($this->getAlias()); 30 | } 31 | 32 | /** 33 | * @param mixed[] $mergedConfig 34 | */ 35 | protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void 36 | { 37 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 38 | 39 | $loader->load('command_bus.yml'); 40 | $loader->load('command_bus_logging.yml'); 41 | 42 | $container->setAlias( 43 | 'simple_bus.command_bus.command_name_resolver', 44 | 'simple_bus.command_bus.'.$mergedConfig['command_name_resolver_strategy'].'_command_name_resolver' 45 | ); 46 | 47 | if ($mergedConfig['logging']['enabled']) { 48 | trigger_error( 49 | 'Option "command_bus.logging" is deprecated. Configure "command_bus.middlewares.logger" instead.', 50 | E_USER_DEPRECATED 51 | ); 52 | 53 | $mergedConfig['middlewares']['logger'] = true; 54 | } 55 | 56 | if ($mergedConfig['middlewares']['logger']) { 57 | $container->getDefinition('simple_bus.command_bus.logging_middleware') 58 | ->addTag('command_bus_middleware', ['priority' => -999]) 59 | ->addTag('monolog.logger', ['channel' => 'command_bus']); 60 | } 61 | 62 | if ($mergedConfig['middlewares']['finishes_command_before_handling_next']) { 63 | $container->getDefinition('simple_bus.command_bus.finishes_command_before_handling_next_middleware') 64 | ->addTag('command_bus_middleware', ['priority' => 1000]); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/SimpleBusEventBusBundle.php: -------------------------------------------------------------------------------- 1 | configurationAlias = $alias; 24 | } 25 | 26 | public function build(ContainerBuilder $container): void 27 | { 28 | $this->checkRequirements(['SimpleBusCommandBusBundle'], $container); 29 | 30 | $container->addCompilerPass( 31 | new AutoRegister('event_subscriber', 'subscribes_to'), 32 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 33 | 10 34 | ); 35 | 36 | $container->addCompilerPass( 37 | new ConfigureMiddlewares( 38 | 'event_bus', 39 | 'event_bus_middleware' 40 | ) 41 | ); 42 | 43 | $container->addCompilerPass( 44 | new RegisterMessageRecorders( 45 | 'simple_bus.event_bus.aggregates_recorded_messages', 46 | 'event_recorder' 47 | ) 48 | ); 49 | 50 | $container->addCompilerPass( 51 | new RegisterSubscribers( 52 | 'simple_bus.event_bus.event_subscribers_collection', 53 | 'simple_bus.event_bus.event_subscribers_service_locator', 54 | 'event_subscriber', 55 | 'subscribes_to' 56 | ) 57 | ); 58 | 59 | $container->addCompilerPass( 60 | new AddMiddlewareTags( 61 | 'simple_bus.event_bus.handles_recorded_messages_middleware', 62 | ['command'], 63 | 200 64 | ), 65 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 66 | 150 67 | ); 68 | } 69 | 70 | public function getContainerExtension(): EventBusExtension 71 | { 72 | return new EventBusExtension($this->configurationAlias); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Resources/config/command_bus.yml: -------------------------------------------------------------------------------- 1 | services: 2 | SimpleBus\SymfonyBridge\Bus\CommandBus: 3 | alias: simple_bus.command_bus 4 | 5 | command_bus: 6 | alias: simple_bus.command_bus 7 | public: true 8 | 9 | simple_bus.command_bus: 10 | class: SimpleBus\SymfonyBridge\Bus\CommandBus 11 | public: false 12 | tags: 13 | - { name: message_bus, bus_name: command_bus, type: command, middleware_tag: command_bus_middleware } 14 | 15 | simple_bus.command_bus.delegates_to_message_handler_middleware: 16 | class: SimpleBus\Message\Handler\DelegatesToMessageHandlerMiddleware 17 | public: false 18 | arguments: 19 | - '@simple_bus.command_bus.command_handler_resolver' 20 | tags: 21 | - { name: command_bus_middleware, priority: -1000 } 22 | 23 | simple_bus.command_bus.class_based_command_name_resolver: 24 | class: SimpleBus\Message\Name\ClassBasedNameResolver 25 | public: false 26 | 27 | simple_bus.command_bus.named_message_command_name_resolver: 28 | class: SimpleBus\Message\Name\NamedMessageNameResolver 29 | public: false 30 | 31 | simple_bus.command_bus.callable_resolver: 32 | class: SimpleBus\Message\CallableResolver\ServiceLocatorAwareCallableResolver 33 | public: false 34 | arguments: 35 | - ['@simple_bus.command_bus.command_handler_service_locator', 'get'] 36 | 37 | simple_bus.command_bus.command_handler_service_locator: 38 | class: Symfony\Component\DependencyInjection\ServiceLocator 39 | tags: ['container.service_locator'] 40 | arguments: 41 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 42 | - [] 43 | 44 | simple_bus.command_bus.command_handler_map: 45 | class: SimpleBus\Message\CallableResolver\CallableMap 46 | public: false 47 | arguments: 48 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 49 | - [] 50 | - '@simple_bus.command_bus.callable_resolver' 51 | 52 | simple_bus.command_bus.command_handler_resolver: 53 | class: SimpleBus\Message\Handler\Resolver\NameBasedMessageHandlerResolver 54 | public: false 55 | arguments: 56 | - '@simple_bus.command_bus.command_name_resolver' 57 | - '@simple_bus.command_bus.command_handler_map' 58 | 59 | simple_bus.command_bus.finishes_command_before_handling_next_middleware: 60 | class: SimpleBus\Message\Bus\Middleware\FinishesHandlingMessageBeforeHandlingNext 61 | public: false 62 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-bus/symfony-bridge", 3 | "type": "symfony-bundle", 4 | "description": "Bridge for using command buses and event buses in Symfony projects", 5 | "keywords": [ 6 | "Symfony", 7 | "Doctrine", 8 | "event bus", 9 | "command bus" 10 | ], 11 | "homepage": "http://github.com/SimpleBus/SymfonyBridge", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Cliff Odijk", 16 | "email": "cliff@jcid.nl" 17 | }, 18 | { 19 | "name": "Ruud Kamphuis", 20 | "homepage": "https://github.com/ruudk" 21 | }, 22 | { 23 | "name": "Matthias Noback", 24 | "email": "matthiasnoback@gmail.com", 25 | "homepage": "http://php-and-symfony.matthiasnoback.nl" 26 | } 27 | ], 28 | "require": { 29 | "php": "^8.0", 30 | "simple-bus/message-bus": "^6.0", 31 | "symfony/config": "^5.4 || ^6.0", 32 | "symfony/dependency-injection": "^5.4 || ^6.0", 33 | "symfony/http-kernel": "^5.4 || ^6.0", 34 | "symfony/yaml": "^5.4 || ^6.0" 35 | }, 36 | "conflict": { 37 | "doctrine/dbal": "<2.13.3", 38 | "doctrine/persistence": "<2.2.2", 39 | "friendsofphp/proxy-manager-lts": "<1.0.5", 40 | "monolog/monolog": "<1.26.1 || >=2.0,<2.3.0", 41 | "zendframework/zend-code": "<3.3.1" 42 | }, 43 | "require-dev": { 44 | "doctrine/doctrine-bundle": "^2.5", 45 | "doctrine/orm": "^2.8.5", 46 | "ergebnis/composer-normalize": "^2.11", 47 | "friendsofphp/proxy-manager-lts": "^1.0", 48 | "laminas/laminas-code": "^4.5", 49 | "phpunit/phpunit": "^9.5.5", 50 | "simple-bus/doctrine-orm-bridge": "^6.0", 51 | "symfony/framework-bundle": "^5.4 || ^6.0", 52 | "symfony/monolog-bridge": "^5.4 || ^6.0", 53 | "symfony/monolog-bundle": "^3.4", 54 | "symfony/phpunit-bridge": "^6.0", 55 | "symfony/proxy-manager-bridge": "^5.4 || ^6.0" 56 | }, 57 | "suggest": { 58 | "doctrine/doctrine-bundle": "For integration with Doctrine ORM", 59 | "doctrine/orm": "For integration with Doctrine ORM", 60 | "simple-bus/doctrine-orm-bridge": "For integration with Doctrine ORM", 61 | "symfony/monolog-bundle": "For logging messages", 62 | "symfony/proxy-manager-bridge": "For integration with Symfony and Doctrine ORM" 63 | }, 64 | "config": { 65 | "sort-packages": true 66 | }, 67 | "autoload": { 68 | "psr-4": { 69 | "SimpleBus\\SymfonyBridge\\": "src" 70 | } 71 | }, 72 | "autoload-dev": { 73 | "psr-4": { 74 | "SimpleBus\\SymfonyBridge\\Tests\\": "tests" 75 | } 76 | }, 77 | "minimum-stability": "dev", 78 | "prefer-stable": true 79 | } 80 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/AddMiddlewareTags.php: -------------------------------------------------------------------------------- 1 | middlewareServiceId = $middlewareServiceId; 28 | $this->addTagForMessageBusesOfTypes = $addTagForMessageBusesOfTypes; 29 | $this->middlewarePriority = $middlewarePriority; 30 | } 31 | 32 | public function process(ContainerBuilder $container): void 33 | { 34 | if (!($container->has($this->middlewareServiceId))) { 35 | return; 36 | } 37 | 38 | $transactionalMiddlewareService = $container->findDefinition($this->middlewareServiceId); 39 | 40 | foreach ($container->findTaggedServiceIds(self::MESSAGE_BUS_TAG) as $serviceId => $tags) { 41 | foreach ($tags as $tagAttributes) { 42 | $type = $this->getAttribute($tagAttributes, 'type', self::MESSAGE_BUS_TAG, $serviceId); 43 | if (!in_array($type, $this->addTagForMessageBusesOfTypes)) { 44 | continue; 45 | } 46 | 47 | $middlewareTag = $this->getAttribute( 48 | $tagAttributes, 49 | 'middleware_tag', 50 | self::MESSAGE_BUS_TAG, 51 | $serviceId 52 | ); 53 | 54 | /* 55 | * This is equivalent to: 56 | * 57 | * services: 58 | * %transactional_middleware_service_id%: 59 | * ... 60 | * tags: 61 | * - { name: %middleware_tag%, priority: %priority% } 62 | */ 63 | $transactionalMiddlewareService->addTag($middlewareTag, ['priority' => $this->middlewarePriority]); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * @param array $tagAttributes 70 | */ 71 | private function getAttribute(array $tagAttributes, string $attribute, string $tag, string $serviceId): string 72 | { 73 | if (!isset($tagAttributes[$attribute])) { 74 | throw new LogicException(sprintf('Tag "%s" of service "%s" should have an attribute "%s"', $tag, $serviceId, $attribute)); 75 | } 76 | 77 | return $tagAttributes[$attribute]; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RegisterHandlers.php: -------------------------------------------------------------------------------- 1 | callableServiceId = $callableServiceId; 27 | $this->serviceLocatorId = $serviceLocatorId; 28 | $this->tag = $tag; 29 | $this->keyAttribute = $keyAttribute; 30 | } 31 | 32 | /** 33 | * Search for message handler services and provide them as a constructor argument to the message handler map 34 | * service. 35 | */ 36 | public function process(ContainerBuilder $container): void 37 | { 38 | if (!$container->has($this->callableServiceId)) { 39 | return; 40 | } 41 | 42 | if (!$container->has($this->serviceLocatorId)) { 43 | return; 44 | } 45 | 46 | $callableDefinition = $container->findDefinition($this->callableServiceId); 47 | $serviceLocatorDefinition = $container->findDefinition($this->serviceLocatorId); 48 | 49 | $handlers = []; 50 | $services = []; 51 | 52 | $this->collectServiceIds( 53 | $container, 54 | $this->tag, 55 | $this->keyAttribute, 56 | function ($key, $serviceId, array $tagAttributes) use (&$handlers, &$services) { 57 | if (isset($tagAttributes['method'])) { 58 | // Symfony 3.3 supports services by classname. This interferes with `is_callable` 59 | // in `ServiceLocatorAwareCallableResolver` 60 | $callable = [ 61 | 'serviceId' => $serviceId, 62 | 'method' => $tagAttributes['method'], 63 | ]; 64 | } else { 65 | $callable = $serviceId; 66 | } 67 | 68 | $handlers[ltrim($key, '\\')] = $callable; 69 | $services[$serviceId] = new Reference($serviceId); 70 | } 71 | ); 72 | 73 | $callableDefinition->replaceArgument(0, $handlers); 74 | $serviceLocatorDefinition->replaceArgument(0, $services); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/RegisterSubscribers.php: -------------------------------------------------------------------------------- 1 | callableServiceId = $callableServiceId; 27 | $this->serviceLocatorId = $serviceLocatorId; 28 | $this->tag = $tag; 29 | $this->keyAttribute = $keyAttribute; 30 | } 31 | 32 | /** 33 | * Search for message subscriber services and provide them as a constructor argument to the message subscriber 34 | * collection service. 35 | */ 36 | public function process(ContainerBuilder $container): void 37 | { 38 | if (!$container->has($this->callableServiceId)) { 39 | return; 40 | } 41 | 42 | if (!$container->has($this->serviceLocatorId)) { 43 | return; 44 | } 45 | 46 | $callableDefinition = $container->findDefinition($this->callableServiceId); 47 | $serviceLocatorDefinition = $container->findDefinition($this->serviceLocatorId); 48 | 49 | $handlers = []; 50 | $services = []; 51 | 52 | $this->collectServiceIds( 53 | $container, 54 | $this->tag, 55 | $this->keyAttribute, 56 | function ($key, $serviceId, array $tagAttributes) use (&$handlers, &$services) { 57 | if (isset($tagAttributes['method'])) { 58 | // Symfony 3.3 supports services by classname. This interferes with `is_callable` 59 | // in `ServiceLocatorAwareCallableResolver` 60 | $callable = [ 61 | 'serviceId' => $serviceId, 62 | 'method' => $tagAttributes['method'], 63 | ]; 64 | } else { 65 | $callable = $serviceId; 66 | } 67 | 68 | $handlers[ltrim($key, '\\')][] = $callable; 69 | $services[$serviceId] = new Reference($serviceId); 70 | } 71 | ); 72 | 73 | $callableDefinition->replaceArgument(0, $handlers); 74 | $serviceLocatorDefinition->replaceArgument(0, $services); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/AutoRegister.php: -------------------------------------------------------------------------------- 1 | tagName = $tagName; 20 | $this->tagAttribute = $tagAttribute; 21 | } 22 | 23 | public function process(ContainerBuilder $container): void 24 | { 25 | foreach ($container->findTaggedServiceIds($this->tagName) as $serviceId => $tags) { 26 | foreach ($tags as $tagAttributes) { 27 | // if tag attribute is set, skip 28 | if (isset($tagAttributes[$this->tagAttribute])) { 29 | continue; 30 | } 31 | 32 | $registerPublicMethods = false; 33 | if (isset($tagAttributes['register_public_methods']) && true === $tagAttributes['register_public_methods']) { 34 | $registerPublicMethods = true; 35 | } 36 | 37 | $definition = $container->getDefinition($serviceId); 38 | 39 | // check if service id is class name 40 | $class = $definition->getClass() ?: $serviceId; 41 | if (!class_exists($class)) { 42 | continue; 43 | } 44 | 45 | $reflectionClass = new ReflectionClass($class); 46 | 47 | $methods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC); 48 | 49 | $tagAttributes = []; 50 | foreach ($methods as $method) { 51 | if (true === $method->isConstructor()) { 52 | continue; 53 | } 54 | 55 | if (true === $method->isDestructor()) { 56 | continue; 57 | } 58 | 59 | if (false === $registerPublicMethods && '__invoke' !== $method->getName()) { 60 | continue; 61 | } 62 | 63 | $parameters = $method->getParameters(); 64 | 65 | // if no param, optional param or non-class param, skip 66 | if (1 !== count($parameters) || $parameters[0]->isOptional()) { 67 | continue; 68 | } 69 | 70 | $type = $parameters[0]->getType(); 71 | if (null === $type) { 72 | continue; 73 | } 74 | 75 | if (!$type instanceof ReflectionNamedType && !$type instanceof ReflectionUnionType) { 76 | continue; 77 | } 78 | 79 | if ($type instanceof ReflectionUnionType) { 80 | $types = $type->getTypes(); 81 | } else { 82 | $types = [$type]; 83 | } 84 | 85 | foreach ($types as $type) { 86 | if (!$type instanceof ReflectionNamedType) { 87 | continue; 88 | } 89 | 90 | $tagAttributes[] = [ 91 | $this->tagAttribute => $type->getName(), 92 | 'method' => $method->getName(), 93 | ]; 94 | } 95 | } 96 | 97 | if (0 !== count($tags)) { 98 | // auto handle 99 | $definition->clearTag($this->tagName); 100 | 101 | foreach ($tagAttributes as $attributes) { 102 | $definition->addTag($this->tagName, $attributes); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Resources/config/event_bus.yml: -------------------------------------------------------------------------------- 1 | services: 2 | SimpleBus\SymfonyBridge\Bus\EventBus: 3 | alias: simple_bus.event_bus 4 | 5 | event_bus: 6 | alias: simple_bus.event_bus 7 | public: true 8 | 9 | simple_bus.event_bus: 10 | class: SimpleBus\SymfonyBridge\Bus\EventBus 11 | public: false 12 | tags: 13 | - { name: message_bus, bus_name: command_bus, type: event, middleware_tag: event_bus_middleware } 14 | 15 | simple_bus.event_bus.events.finishes_message_before_handling_next_middleware: 16 | class: SimpleBus\Message\Bus\Middleware\FinishesHandlingMessageBeforeHandlingNext 17 | public: false 18 | tags: 19 | - { name: event_bus_middleware, priority: 1000 } 20 | 21 | simple_bus.event_bus.aggregates_recorded_messages: 22 | class: SimpleBus\Message\Recorder\AggregatesRecordedMessages 23 | public: false 24 | arguments: 25 | # collection of RecordsMessages instances, will be provided by the RegisterMessageRecorders compiler pass 26 | - [] 27 | 28 | SimpleBus\Message\Recorder\RecordsMessages: 29 | alias: 'event_recorder' 30 | 31 | event_recorder: 32 | alias: simple_bus.event_bus.public_event_recorder 33 | public: false 34 | 35 | simple_bus.event_bus.public_event_recorder: 36 | class: SimpleBus\Message\Recorder\PublicMessageRecorder 37 | public: false 38 | tags: 39 | - { name: event_recorder } 40 | 41 | simple_bus.event_bus.notifies_message_subscribers_middleware: 42 | class: SimpleBus\Message\Subscriber\NotifiesMessageSubscribersMiddleware 43 | public: false 44 | arguments: 45 | - '@simple_bus.event_bus.event_subscribers_resolver' 46 | - ~ # can be a logger, may be provided by EventBusExtension when logging enabled 47 | - ~ # can be the logging level, may be provided by EventBusExtension when logging enabled 48 | tags: 49 | - { name: event_bus_middleware, priority: -1000 } 50 | 51 | simple_bus.event_bus.class_based_event_name_resolver: 52 | class: SimpleBus\Message\Name\ClassBasedNameResolver 53 | public: false 54 | 55 | simple_bus.event_bus.named_message_event_name_resolver: 56 | class: SimpleBus\Message\Name\NamedMessageNameResolver 57 | public: false 58 | 59 | simple_bus.event_bus.callable_resolver: 60 | class: SimpleBus\Message\CallableResolver\ServiceLocatorAwareCallableResolver 61 | public: false 62 | arguments: 63 | - ['@simple_bus.event_bus.event_subscribers_service_locator', 'get'] 64 | 65 | simple_bus.event_bus.event_subscribers_service_locator: 66 | class: Symfony\Component\DependencyInjection\ServiceLocator 67 | tags: ['container.service_locator'] 68 | arguments: 69 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 70 | - [] 71 | 72 | simple_bus.event_bus.event_subscribers_collection: 73 | class: SimpleBus\Message\CallableResolver\CallableCollection 74 | public: false 75 | arguments: 76 | # collection of event subscriber services ids, will be provided by the RegisterSubscribers compiler pas 77 | - [] 78 | - '@simple_bus.event_bus.callable_resolver' 79 | 80 | SimpleBus\Message\Subscriber\Resolver\MessageSubscribersResolver: 81 | alias: 'simple_bus.event_bus.event_subscribers_resolver' 82 | 83 | simple_bus.event_bus.event_subscribers_resolver: 84 | class: SimpleBus\Message\Subscriber\Resolver\NameBasedMessageSubscriberResolver 85 | public: false 86 | arguments: 87 | - '@simple_bus.event_bus.event_name_resolver' 88 | - '@simple_bus.event_bus.event_subscribers_collection' 89 | 90 | simple_bus.event_bus.handles_recorded_messages_middleware: 91 | class: SimpleBus\Message\Recorder\HandlesRecordedMessagesMiddleware 92 | public: false 93 | arguments: 94 | - '@simple_bus.event_bus.aggregates_recorded_messages' 95 | - '@simple_bus.event_bus' 96 | 97 | simple_bus.event_bus.handles_recorded_mesages_middleware: 98 | alias: simple_bus.event_bus.handles_recorded_messages_middleware 99 | public: false 100 | --------------------------------------------------------------------------------