Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async commands #57

Open
javaDeveloperKid opened this issue Apr 7, 2024 · 2 comments
Open

Add async commands #57

javaDeveloperKid opened this issue Apr 7, 2024 · 2 comments

Comments

@javaDeveloperKid
Copy link

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;
        }
    }
}
@yceruto
Copy link
Contributor

yceruto commented Apr 11, 2024

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.

@javaDeveloperKid
Copy link
Author

javaDeveloperKid commented Apr 11, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants