Skip to main content

Handlers

A handler is the callable that does the work a message asks for. A MessageHandlerRegistry holds one handler per message type, and a worker asks it for the handler of each message it takes off the queue:

use Dirthara\Queue\MessageHandlerRegistry;

$handlers = new MessageHandlerRegistry();

$handlers->register(SendWelcomeEmail::class, static function (SendWelcomeEmail $message) use ($mailer): void {
$mailer->sendWelcomeEmail($message->email);
});

$handlers->register(GenerateInvoice::class, $invoices->generate(...));

A handler receives the deserialised message and returns nothing. It knows nothing about queues, attempts, retries, or delays: whether a failure is retried is decided by the message's execution policy. A handler reports failure by throwing.

A handler can receive the same message more than once: through retries, and with a durable queue, which may deliver a message again after a worker stopped before acknowledging it. A handler with an effect that must not be repeated, such as charging a card, has to account for that; see delivery guarantees.

Exact message types​

A handler is registered for an exact class or enum, and is only used for messages of that exact class:

  • A handler for a parent class is not used for its subclasses, and a handler for a subclass is not used for its parent.
  • An interface or an abstract class cannot be registered, because no message has one as its exact class. Registering one, or a name that is not a class or enum at all, throws an InvalidMessageTypeException.
  • The class name is matched however it was written: \App\SendWelcomeEmail and app\sendwelcomeemail register the same type as App\SendWelcomeEmail.
  • A message type has at most one handler. Registering a second throws a DuplicateMessageHandlerException and keeps the first.

An enum is registered by its class, and its handler receives the enum case.

A message without a handler​

handlerFor() throws a MessageHandlerNotFoundException for a message whose type has no handler. A worker treats that as a failed attempt and settles it with the message's execution policy, so the message is retried or failed rather than lost.

Depending on the contracts​

Handlers have two contracts, so code depends on only the capability it uses:

ContractMethodsUsed by
Contract\MessageHandlerProviderhandlerFor()The worker, and anything else that only looks handlers up.
Contract\MessageHandlerRegistryhandlerFor() and register()Configuration code that registers handlers.

Dirthara\Queue\MessageHandlerRegistry implements both. It shares its short name with the registry contract, so code that uses both imports one under an alias:

use Dirthara\Queue\Contract\MessageHandlerRegistry as MessageHandlerRegistryContract;
use Dirthara\Queue\MessageHandlerRegistry;

function registerMailHandlers(MessageHandlerRegistryContract $handlers, Mailer $mailer): void
{
$handlers->register(SendWelcomeEmail::class, static function (SendWelcomeEmail $message) use ($mailer): void {
$mailer->sendWelcomeEmail($message->email);
});
}

registerMailHandlers(new MessageHandlerRegistry(), $mailer);