diff --git a/.woodpecker/code-style.yml b/.woodpecker/code-style.yml new file mode 100644 index 0000000..bfd94b0 --- /dev/null +++ b/.woodpecker/code-style.yml @@ -0,0 +1,8 @@ +pipeline: + code-style: + image: composer + commands: + - composer global config repositories.repo-name vcs https://git.digital-competence.de/Packages/php-codesniffer + - composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true + - composer global require digicomp/php-codesniffer:@dev + - composer global exec -- phpcs --runtime-set ignore_warnings_on_exit 1 --standard=DigiComp Classes/ Tests/ diff --git a/.woodpecker/functional-tests.yml b/.woodpecker/functional-tests.yml new file mode 100644 index 0000000..c0a44a5 --- /dev/null +++ b/.woodpecker/functional-tests.yml @@ -0,0 +1,27 @@ +workspace: + base: /woodpecker + path: package + +matrix: + include: + - FLOW_VERSION: 8.2 + PHP_VERSION: 8.1 + +pipeline: + functional-tests: + image: "thecodingmachine/php:${PHP_VERSION}-v4-cli" + environment: + # Enable the PDO_SQLITE extension + - "PHP_EXTENSION_PDO_SQLITE=1" + - "FLOW_VERSION=${FLOW_VERSION}" + - "NEOS_BUILD_DIR=/woodpecker/Build-${FLOW_VERSION}" + commands: + - "sudo mkdir $NEOS_BUILD_DIR" + - "sudo chown -R docker:docker $NEOS_BUILD_DIR" + - "cd $NEOS_BUILD_DIR" + - "composer create-project --no-install neos/flow-base-distribution:^$FLOW_VERSION ." + - "composer config repositories.repo-name path /woodpecker/package" + - "composer config --no-plugins allow-plugins.neos/composer-plugin true" + - "composer remove --dev --no-update neos/behat || composer remove --no-update neos/behat" + - "composer require digicomp/flow-symfony-bridge-messenger:@dev" + - "bin/phpunit --configuration Build/BuildEssentials/PhpUnit/FunctionalTests.xml Packages/Application/DigiComp.FlowSymfonyBridge.Messenger/Tests/Functional" diff --git a/Classes/Command/FailedCommandController.php b/Classes/Command/FailedCommandController.php index 9b927b2..adb2337 100644 --- a/Classes/Command/FailedCommandController.php +++ b/Classes/Command/FailedCommandController.php @@ -14,16 +14,10 @@ class FailedCommandController extends CommandController { use RunSymfonyCommandTrait; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:ReceiversContainer") - * @var ContainerInterface - */ - protected $receiverContainer; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:ReceiversContainer')] + protected ContainerInterface $receiverContainer; - /** - * @Flow\InjectConfiguration - * @var array - */ + #[Flow\InjectConfiguration] protected array $configuration; /** @@ -39,7 +33,7 @@ class FailedCommandController extends CommandController * * Optional arguments are -q (quiet) -v[v[v]] (verbosity) and --force (do not ask) */ - public function showCommand() + public function showCommand(): void { $command = new FailedMessagesShowCommand( $this->configuration['failureTransport'], @@ -59,7 +53,7 @@ class FailedCommandController extends CommandController * * Optional arguments are -q (quiet) -v[v[v]] (verbosity) and --force (do not ask) */ - public function removeCommand() + public function removeCommand(): void { $command = new FailedMessagesRemoveCommand( $this->configuration['failureTransport'], @@ -87,7 +81,7 @@ class FailedCommandController extends CommandController * * @noinspection PhpParamsInspection */ - public function retryCommand() + public function retryCommand(): void { $command = new FailedMessagesRetryCommand( $this->configuration['failureTransport'], diff --git a/Classes/Command/MessengerCommandController.php b/Classes/Command/MessengerCommandController.php index de0930f..ade916b 100644 --- a/Classes/Command/MessengerCommandController.php +++ b/Classes/Command/MessengerCommandController.php @@ -17,47 +17,29 @@ class MessengerCommandController extends CommandController { use RunSymfonyCommandTrait; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:RoutableMessageBus") - * @var RoutableMessageBus - */ - protected $routableBus; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:RoutableMessageBus')] + protected RoutableMessageBus $routableBus; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:ReceiversContainer") - * @var ContainerInterface - */ - protected $receiverContainer; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:ReceiversContainer')] + protected ContainerInterface $receiverContainer; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:EventDispatcher") - * @var EventDispatcherInterface - */ - protected $eventDispatcher; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:EventDispatcher')] + protected EventDispatcherInterface $eventDispatcher; - /** - * @Flow\Inject(lazy=false) - * @var LoggerInterface - */ + #[Flow\Inject] protected LoggerInterface $logger; - /** - * @Flow\InjectConfiguration - * @var array - */ + #[Flow\InjectConfiguration] protected array $configuration; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:RestartSignalCachePool") - * @var CacheItemPoolInterface - */ - protected $restartSignalCachePool; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:RestartSignalCachePool')] + protected CacheItemPoolInterface $restartSignalCachePool; /** * Consumes messages and dispatches them to the message bus * * To receive from multiple transports, pass each name: - * worker:consume receiver1 receiver2 + * messenger:consume receiver1 receiver2 * * Options are: * --limit limits the number of messages received @@ -71,7 +53,7 @@ class MessengerCommandController extends CommandController * * Optional arguments are -q (quiet) and -v[v[v]] (verbosity) */ - public function consumeCommand() + public function consumeCommand(): void { if ($this->receiverContainer instanceof DependencyProxy) { $this->receiverContainer->_activateDependency(); @@ -92,7 +74,7 @@ class MessengerCommandController extends CommandController /** * List all available receivers */ - public function listReceiversCommand() + public function listReceiversCommand(): void { foreach (\array_keys($this->configuration['transports']) as $transportName) { $this->outputLine('- ' . $transportName); @@ -106,7 +88,7 @@ class MessengerCommandController extends CommandController * and then exit. Worker commands are *not* automatically restarted: that * should be handled by a process control system. */ - public function stopWorkersCommand() + public function stopWorkersCommand(): void { $cacheItem = $this->restartSignalCachePool->getItem( StopWorkerOnRestartSignalListener::RESTART_REQUESTED_TIMESTAMP_KEY diff --git a/Classes/Command/RunSymfonyCommandTrait.php b/Classes/Command/RunSymfonyCommandTrait.php index b5e4779..a64bfa5 100644 --- a/Classes/Command/RunSymfonyCommandTrait.php +++ b/Classes/Command/RunSymfonyCommandTrait.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; trait RunSymfonyCommandTrait { - protected function run(Command $command) + protected function run(Command $command): void { $definition = $command->getDefinition(); $definition->setArguments(\array_merge( @@ -29,7 +29,7 @@ trait RunSymfonyCommandTrait $command->run($input, $this->output->getOutput()); } - protected function configureIO($input, $output) + protected function configureIO($input, $output): void { switch ($shellVerbosity = (int)\getenv('SHELL_VERBOSITY')) { case -1: diff --git a/Classes/EventDispatcherFactory.php b/Classes/EventDispatcherFactory.php index 99fccad..fae1fa7 100644 --- a/Classes/EventDispatcherFactory.php +++ b/Classes/EventDispatcherFactory.php @@ -11,19 +11,13 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; // TODO: Maybe an own package for EntityManager bridge? class EventDispatcherFactory { - /** - * @Flow\Inject - * @var ObjectManagerInterface - */ - protected $objectManager; + #[Flow\Inject(lazy: false)] + protected ObjectManagerInterface $objectManager; - /** - * @Flow\InjectConfiguration - * @var array - */ - protected $configuration; + #[Flow\InjectConfiguration] + protected array $configuration; - public function create() + public function create(): EventDispatcher { $eventDispatcher = new EventDispatcher(); @@ -36,7 +30,7 @@ class EventDispatcherFactory return $eventDispatcher; } - private function addLazySubscribers(EventDispatcherInterface $eventDispatcher, $subscriberId) + private function addLazySubscribers(EventDispatcherInterface $eventDispatcher, $subscriberId): void { $subscriberClass = $this->objectManager->getClassNameByObjectName($subscriberId); if (! \is_a($subscriberClass, EventSubscriberInterface::class, true)) { diff --git a/Classes/EventListener/StopWorkerOnRestartSignalListener.php b/Classes/EventListener/StopWorkerOnRestartSignalListener.php index 9d878a4..444decc 100644 --- a/Classes/EventListener/StopWorkerOnRestartSignalListener.php +++ b/Classes/EventListener/StopWorkerOnRestartSignalListener.php @@ -11,27 +11,19 @@ use Symfony\Component\Messenger\Event\WorkerStartedEvent; // This is a 1 to one copy of the original event listener, with a modified RESTART_REQUESTED_TIMESTAMP_KEY to match // the restriction of the cache ids in flow. -// Also the DI is simplified +// Additionally, the DI is simplified -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope('singleton')] class StopWorkerOnRestartSignalListener implements EventSubscriberInterface { public const RESTART_REQUESTED_TIMESTAMP_KEY = 'workers_restart_requested_timestamp'; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:RestartSignalCachePool") - * @var CacheItemPoolInterface - */ - protected $cachePool; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:RestartSignalCachePool', lazy: false)] + protected CacheItemPoolInterface $cachePool; - /** - * @Flow\Inject - * @var LoggerInterface - */ - protected $logger; - private $workerStartedAt; + #[Flow\Inject(lazy: false)] + protected LoggerInterface $logger; + private float $workerStartedAt; public function onWorkerStarted(): void { @@ -42,13 +34,11 @@ class StopWorkerOnRestartSignalListener implements EventSubscriberInterface { if ($this->shouldRestart()) { $event->getWorker()->stop(); - if (null !== $this->logger) { - $this->logger->info('Worker stopped because a restart was requested.'); - } + $this->logger->info('Worker stopped because a restart was requested.'); } } - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { return [ WorkerStartedEvent::class => 'onWorkerStarted', diff --git a/Classes/HandlersLocatorFactory.php b/Classes/HandlersLocatorFactory.php index a060ab9..4956257 100644 --- a/Classes/HandlersLocatorFactory.php +++ b/Classes/HandlersLocatorFactory.php @@ -5,31 +5,23 @@ namespace DigiComp\FlowSymfonyBridge\Messenger; use Neos\Flow\Annotations as Flow; use Neos\Flow\ObjectManagement\ObjectManagerInterface; use Neos\Flow\Reflection\ReflectionService; +use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; class HandlersLocatorFactory { - /** - * @Flow\InjectConfiguration - * @var array - */ - protected $configuration; + #[Flow\InjectConfiguration] + protected array $configuration; - /** - * @Flow\Inject - * @var ObjectManagerInterface - */ - protected $objectManager; + #[Flow\Inject(lazy: false)] + protected ObjectManagerInterface $objectManager; - /** - * @Flow\Inject - * @var ReflectionService - */ - protected $reflectionService; + #[Flow\Inject(lazy: false)] + protected ReflectionService $reflectionService; - public function create($busName = 'default') + public function create($busName = 'default'): HandlersLocator { $messageHandlerClasses = $this->reflectionService ->getAllImplementationClassNamesForInterface(MessageSubscriberInterface::class); @@ -50,6 +42,30 @@ class HandlersLocatorFactory ); } } + $asHandlerClasses = $this->reflectionService + ->getClassNamesByAnnotation(AsMessageHandler::class); + foreach ($asHandlerClasses as $asHandlerClass) { + /** @var AsMessageHandler[] $annotations */ + $annotations = $this->reflectionService->getClassAnnotations($asHandlerClass, AsMessageHandler::class); + foreach ($annotations as $annotation) { + $config['from_transport'] = $annotation->fromTransport; + $config['priority'] = $annotation->priority; + $method = $annotation->method ?? '__invoke'; + $messageName = $annotation->handles; + if ($messageName === null) { + $arguments = $this->reflectionService->getMethodParameters($asHandlerClass, $method); + $messageName = $arguments[\array_key_first($arguments)]['class']; + } + if ($annotation->bus !== null && $annotation->bus !== $busName) { + continue; + } + $handler = $this->objectManager->get($asHandlerClass); + $handlerDescriptors[$messageName][] = new HandlerDescriptor( + $this->objectManager->get($asHandlerClass), + $config + ); + } + } // TODO: Maybe we can allow handlers to be added to bus or globally by configuration? return new HandlersLocator($handlerDescriptors); diff --git a/Classes/MessageBusContainer.php b/Classes/MessageBusContainer.php index e461003..5991da2 100644 --- a/Classes/MessageBusContainer.php +++ b/Classes/MessageBusContainer.php @@ -7,25 +7,17 @@ use Neos\Flow\Annotations as Flow; use Psr\Container\ContainerInterface; use Symfony\Component\Messenger\MessageBus; -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope('singleton')] class MessageBusContainer implements ContainerInterface { - /** - * @Flow\InjectConfiguration(path="buses") - * @var array - */ - protected $configuration; + #[Flow\InjectConfiguration(path: 'buses')] + protected array $configuration; /** * @var MessageBus[] */ protected array $buses = []; - /** - * @inheritDoc - */ public function get(string $id) { if (! isset($this->buses[$id])) { @@ -35,10 +27,7 @@ class MessageBusContainer implements ContainerInterface return $this->buses[$id]; } - /** - * @inheritDoc - */ - public function has(string $id) + public function has(string $id): bool { return isset($this->configuration[$id]); } diff --git a/Classes/ObjectManagement/ChainedContainer.php b/Classes/ObjectManagement/ChainedContainer.php index 22cd004..ace8ee5 100644 --- a/Classes/ObjectManagement/ChainedContainer.php +++ b/Classes/ObjectManagement/ChainedContainer.php @@ -23,7 +23,7 @@ class ChainedContainer implements ContainerInterface throw new \InvalidArgumentException('Service id is unknown: ' . $id); } - public function has(string $id) + public function has(string $id): bool { foreach ($this->childContainers as $childContainer) { if ($childContainer->has($id)) { diff --git a/Classes/ObjectManagement/RewindableGenerator.php b/Classes/ObjectManagement/RewindableGenerator.php index e6fe50b..869d132 100644 --- a/Classes/ObjectManagement/RewindableGenerator.php +++ b/Classes/ObjectManagement/RewindableGenerator.php @@ -15,11 +15,8 @@ use Psr\Log\LoggerInterface; */ class RewindableGenerator implements \IteratorAggregate, \Countable { - /** - * @Flow\Inject - * @var ObjectManagerInterface - */ - protected $objectManager; + #[Flow\Inject(lazy: false)] + protected ObjectManagerInterface $objectManager; private array $serviceIds; @@ -46,14 +43,14 @@ class RewindableGenerator implements \IteratorAggregate, \Countable }; } - public function getIterator() + public function getIterator(): \Traversable { $g = $this->generator; return $g(); } - public function count() + public function count(): int { return \count($this->serviceIds); } diff --git a/Classes/RetryStrategiesContainer.php b/Classes/RetryStrategiesContainer.php index 6041c9f..6beead2 100644 --- a/Classes/RetryStrategiesContainer.php +++ b/Classes/RetryStrategiesContainer.php @@ -8,22 +8,14 @@ use Psr\Container\ContainerInterface; use Symfony\Component\Messenger\Retry\MultiplierRetryStrategy; use Symfony\Component\Messenger\Retry\RetryStrategyInterface; -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope('singleton')] class RetryStrategiesContainer implements ContainerInterface { - /** - * @Flow\InjectConfiguration - * @var array - */ + #[Flow\InjectConfiguration] protected array $configuration; - /** - * @Flow\Inject - * @var ObjectManagerInterface - */ - protected $objectManager; + #[Flow\Inject] + protected ObjectManagerInterface $objectManager; /** * @var RetryStrategyInterface[] @@ -54,7 +46,7 @@ class RetryStrategiesContainer implements ContainerInterface return $this->retryStrategies[$id]; } - public function has(string $id) + public function has(string $id): bool { return isset($this->configuration['transports'][$id]); } diff --git a/Classes/Transport/FailureTransportContainer.php b/Classes/Transport/FailureTransportContainer.php new file mode 100644 index 0000000..3b86a3c --- /dev/null +++ b/Classes/Transport/FailureTransportContainer.php @@ -0,0 +1,31 @@ +transports[$id]; + } + + public function has(string $id): bool + { + return isset($this->transports[$id]); + } + + public function set(string $id, TransportInterface $transport): void + { + $this->transports[$id] = $transport; + } +} diff --git a/Classes/Transport/FlowDoctrineTransportFactory.php b/Classes/Transport/FlowDoctrineTransportFactory.php index f9c4d9d..b23cb31 100644 --- a/Classes/Transport/FlowDoctrineTransportFactory.php +++ b/Classes/Transport/FlowDoctrineTransportFactory.php @@ -13,9 +13,7 @@ use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope('singleton')] class FlowDoctrineTransportFactory implements TransportFactoryInterface { private EntityManagerInterface $entityManager; @@ -51,11 +49,8 @@ class FlowDoctrineTransportFactory implements TransportFactoryInterface return new DoctrineTransport($connection, $serializer); } - /** - * @inheritDoc - */ public function supports(string $dsn, array $options): bool { - return 0 === \strpos($dsn, 'flow-doctrine://'); + return \str_starts_with($dsn, 'flow-doctrine://'); } } diff --git a/Classes/Transport/NullTransport.php b/Classes/Transport/NullTransport.php index 77973af..e84c4e2 100644 --- a/Classes/Transport/NullTransport.php +++ b/Classes/Transport/NullTransport.php @@ -7,33 +7,21 @@ use Symfony\Component\Messenger\Transport\TransportInterface; class NullTransport implements TransportInterface { - /** - * @inheritDoc - */ public function get(): iterable { return new \EmptyIterator(); } - /** - * @inheritDoc - */ public function ack(Envelope $envelope): void { // do nothing } - /** - * @inheritDoc - */ public function reject(Envelope $envelope): void { // do nothing } - /** - * @inheritDoc - */ public function send(Envelope $envelope): Envelope { return $envelope; diff --git a/Classes/Transport/NullTransportFactory.php b/Classes/Transport/NullTransportFactory.php index 4915998..7856cf5 100644 --- a/Classes/Transport/NullTransportFactory.php +++ b/Classes/Transport/NullTransportFactory.php @@ -8,19 +8,13 @@ use Symfony\Component\Messenger\Transport\TransportInterface; class NullTransportFactory implements TransportFactoryInterface { - /** - * @inheritDoc - */ public function createTransport(string $dsn, array $options, SerializerInterface $serializer): TransportInterface { return new NullTransport(); } - /** - * @inheritDoc - */ public function supports(string $dsn, array $options): bool { - return 0 === \strpos($dsn, 'null://'); + return \str_starts_with($dsn, 'null://'); } } diff --git a/Classes/Transport/TransportsContainer.php b/Classes/Transport/TransportsContainer.php index d96a752..7eb192f 100644 --- a/Classes/Transport/TransportsContainer.php +++ b/Classes/Transport/TransportsContainer.php @@ -8,28 +8,20 @@ use Psr\Container\ContainerInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope('singleton')] class TransportsContainer implements ContainerInterface { - /** - * @Flow\InjectConfiguration - * @var array - */ + #[Flow\InjectConfiguration] protected array $configuration; - /** - * @Flow\Inject - * @var ObjectManagerInterface - */ - protected $objectManager; + #[Flow\Inject(lazy: false)] + protected ObjectManagerInterface $objectManager; - /** - * @Flow\Inject(name="DigiComp.FlowSymfonyBridge.Messenger:TransportFactory") - * @var TransportFactoryInterface - */ - protected $transportFactory; + #[Flow\Inject(name: 'DigiComp.FlowSymfonyBridge.Messenger:TransportFactory', lazy: false)] + protected TransportFactoryInterface $transportFactory; + + #[Flow\Inject(lazy: false)] + protected FailureTransportContainer $failureTransports; /** * @var TransportInterface[] @@ -65,11 +57,16 @@ class TransportsContainer implements ContainerInterface $transportDefinition['options'], $this->objectManager->get($transportDefinition['serializer']) ); + if (isset($transportDefinition['failureTransport'])) { + $this->failureTransports->set($id, $this->get($transportDefinition['failureTransport'])); + } elseif (isset($this->configuration['failureTransport'])) { + $this->failureTransports->set($id, $this->get($this->configuration['failureTransport'])); + } } return $this->transports[$id]; } - public function has(string $id) + public function has(string $id): bool { return isset($this->configuration['transports'][$id]); } diff --git a/Configuration/Objects.yaml b/Configuration/Objects.yaml index 380de1e..6e2d395 100644 --- a/Configuration/Objects.yaml +++ b/Configuration/Objects.yaml @@ -89,6 +89,13 @@ DigiComp.FlowSymfonyBridge.Messenger:ReceiversContainer: object: 'DigiComp\FlowSymfonyBridge\Messenger\Transport\TransportsContainer' # TODO: add own receivers here, which are no transports +DigiComp.FlowSymfonyBridge.Messenger:FailureSenderContainer: + className: 'DigiComp\FlowSymfonyBridge\Messenger\ObjectManagement\ChainedContainer' + scope: 'singleton' + arguments: + 1: + object: 'DigiComp\FlowSymfonyBridge\Messenger\Transport\FailureTransportContainer' + DigiComp.FlowSymfonyBridge.Messenger:EventDispatcher: className: 'Symfony\Component\EventDispatcher\EventDispatcher' scope: 'singleton' @@ -133,10 +140,6 @@ Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportLis arguments: 1: object: - factoryObjectName: 'DigiComp.FlowSymfonyBridge.Messenger:SendersContainer' - factoryMethodName: 'get' - arguments: - 1: - setting: 'DigiComp.FlowSymfonyBridge.Messenger.failureTransport' + name: 'DigiComp.FlowSymfonyBridge.Messenger:FailureSenderContainer' 2: object: 'Psr\Log\LoggerInterface' diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index cf638d5..58df117 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -10,7 +10,6 @@ DigiComp: Messenger: defaultBusName: "default" defaultSerializerName: "DigiComp.FlowSymfonyBridge.Messenger:DefaultSerializer" - # TODO: use this defaultRetryStrategyOptions: maxRetries: 3 # milliseconds delay @@ -19,8 +18,8 @@ DigiComp: # e.g. 1 second delay, 2 seconds, 4 seconds multiplier: 2 maxDelay: 0 - # override all of this with a service that - # implements Symfony\Component\Messenger\Retry\RetryStrategyInterface + # override all of this with a service that implements + # Symfony\Component\Messenger\Retry\RetryStrategyInterface service: null eventDispatcher: @@ -55,7 +54,4 @@ DigiComp: dsn: "null://" failureTransport: "discard" - # TODO: Receivers and Senders? (As far as I can see not possible in Symfony) - # receivers:[] - # senders: [] routing: [] diff --git a/README.md b/README.md new file mode 100644 index 0000000..81be53a --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# DigiComp.FlowSymfonyBridge.Messenger + +![Build status](https://ci.digital-competence.de/api/badges/Packages/DigiComp.FlowSymfonyBridge.Messenger/status.svg) + +This packages brings a DI configuration for the `symfony/messenger` component, so it can be used easily in `neos/flow` projects. + +To see how to use it, you probably want to have a look at the [documentation](https://symfony.com/doc/current/messenger.html) of `symfony/messenger`. + + +## Getting started + +To get it integrated, you all need to do is to get message bus injected: + +```php + #[Flow\Inject] + protected MessageBusInterface $messageBus; +``` + +And later in your method: + +```php + $this->messageBus->dispatch(new CustomMessage()) +``` + +You should configure a routing, to let the messenger know, over which transport your message should be handled: + +```yaml +DigiComp: + FlowSymfonyBridge: + Messenger: + transports: + "custom-messages": + dsn: "flow-doctrine://default?table_name=test_messenger_messages" + routing: + Acme\Vendor\Messenger\CustomMessage: + - "custom-messages" +``` + +In this example we are using a doctrine transport (the speciality "flow-transport" is a transport which uses the already existing connection to doctrine instead of creating a new one - for the rest of the DSN-Format have a look in the documentation of `symfony/messenger`) + +A handler for your CustomMessage could look like this: + +```php +use Symfony\Component\Messenger\Attribute\AsMessageHandler; + +#[AsMessageHandler] +class CustomMessageHandler +{ + public function __invoke(CustomMessage $message) + { + //your code here + } +} +``` + +It will be automatically found by Flow // the messenger and messages arriving at the bus will be handled by your handler. + +Probably you'll want to consume the messengers with long living processes or as a cronjob. The Flow command for that task is `messenger:consume` (more help available) diff --git a/Tests/Functional/Fixtures/Message/TestMessageHandler.php b/Tests/Functional/Fixtures/Message/TestMessageHandler.php index bddf7d0..efb5f38 100644 --- a/Tests/Functional/Fixtures/Message/TestMessageHandler.php +++ b/Tests/Functional/Fixtures/Message/TestMessageHandler.php @@ -2,15 +2,11 @@ namespace DigiComp\FlowSymfonyBridge\Messenger\Tests\Functional\Fixtures\Message; -use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; +use Symfony\Component\Messenger\Attribute\AsMessageHandler; -class TestMessageHandler implements MessageSubscriberInterface +#[AsMessageHandler] +class TestMessageHandler { - public static function getHandledMessages(): iterable - { - yield TestMessage::class => []; - } - public function __invoke(TestMessage $message) { //do nothing for now diff --git a/composer.json b/composer.json index 31776bd..cd76787 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,12 @@ { "name": "digicomp/flow-symfony-bridge-messenger", "type": "neos-package", - "license": "MIT", "description": "Flow dependency injection bridge to symfony/messenger", "require": { - "neos/flow": "^6.3", - "symfony/doctrine-messenger": "^5.2.5", - "symfony/event-dispatcher": "^4.2 | ^5.2" + "php": "^8.1", + "neos/flow": "^8.0", + "symfony/doctrine-messenger": "^6.2", + "symfony/event-dispatcher": "^4.2 | ^5.2 | ^6.2" }, "autoload": { "psr-4": { @@ -20,10 +20,26 @@ }, "extra": { "branch-alias": { - "dev-master": "0.0.x-dev" + "dev-master": "0.1.x-dev" }, "neos": { "package-key": "DigiComp.FlowSymfonyBridge.Messenger" } - } + }, + "authors": [ + { + "name": "Ferdinand Kuhl", + "email": "f.kuhl@digital-competence.de", + "homepage": "https://www.digital-competence.de", + "role": "Developer" + } + ], + "license": "MIT", + "homepage": "https://github.com/digital-competence/DigiComp.FlowSymfonyBridge.Messenger", + "keywords": [ + "Neos", + "Flow", + "symfony", + "messenger" + ] }