Add async commands
javaDeveloperKid opened this issue · 2 comments
Hi, I am using this repository as base for my new project. However this repo does not implement async commands and as a consequence it does not show how to consider async commands inside a command bus implementation.
I rewritten the MessengerCommandBus implementation however this does not satisfy PHPStan as returning null for async commands results in error
Method MessengerCommandBus::dispatch() should return T but returns null.
💡 Type null is not always the same as T. It breaks the contract for some argument types, typically subtypes.
final class MessengerCommandBus implements CommandBusInterface
{
public function __construct(MessageBusInterface $commandBus)
{
$this->messageBus = $commandBus;
}
/**
* @template T
*
* @param CommandInterface<T> $command
*
* @return T
*/
public function dispatch(CommandInterface $command): mixed
{
try {
$envelope = $this->messageBus->dispatch($message);
/** @var HandledStamp[] $handledStamps */
$handledStamps = $envelope->all(HandledStamp::class);
if (!$handledStamps) {
// async command
return null;
}
if (\count($handledStamps) > 1) {
$handlers = implode(', ', array_map(fn (HandledStamp $stamp): string => sprintf('"%s"', $stamp->getHandlerName()), $handledStamps));
throw new LogicException(sprintf('Message of type "%s" was handled multiple times. Only one handler is expected when using "%s::%s()", got %d: %s.', get_debug_type($envelope->getMessage()), static::class, __FUNCTION__, \count($handledStamps), $handlers));
}
return $handledStamps[0]->getResult();
} catch (HandlerFailedException $e) {
if ($exception = current($e->getWrappedExceptions())) {
throw $exception;
}
throw $e;
}
}
}
if (!$handledStamps) {
// async command
return null;
}
Note that still a normal sync command must meet a handler. By removing the logic exception you'll miss that rule.
The alternative to allow async without losing the sync validation would be:
if (!$handledStamps) {
if ($envelope->all(SentStamp::class)) {
// async command
return null;
}
throw new LogicException(sprintf('Message of type "%s" was handled zero times. Exactly one handler is expected when using "%s::%s()".', get_debug_type($envelope->getMessage()), static::class, __FUNCTION__));
}
Checking by SentStamp::class
you'll know that the command was at least sent through an async transport.
Thank you for your response 👍 However my problem described above is about PHPStan and generics :)
Note that still a normal sync command must meet a handler. By removing the logic exception you'll miss that rule.
I believe allow_no_handlers: false
in the bus configuration (this is a default in SF Messenger) will take care of that.