symfony/messenger
Symfony Messenger helps PHP apps send and receive messages via async transports and message queues. Dispatch commands/events, route to handlers, and integrate with workers and transports to decouple services and improve scalability.
Installation:
composer require symfony/messenger
For Laravel, use symfony/messenger alongside symfony/amqp-messenger (for AMQP) or symfony/doctrine-messenger (for Doctrine transport).
Define a Message Class:
namespace App\Messages;
class SendEmailMessage
{
public function __construct(public string $email, public string $subject) {}
}
Create a Message Handler:
namespace App\MessageHandlers;
use App\Messages\SendEmailMessage;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
class SendEmailHandler
{
public function __invoke(SendEmailMessage $message)
{
// Logic to send email
}
}
Dispatch a Message:
use Symfony\Component\Messenger\MessageBusInterface;
public function __construct(private MessageBusInterface $bus) {}
public function sendWelcomeEmail(string $email)
{
$this->bus->dispatch(new SendEmailMessage($email, 'Welcome!'));
}
Consume Messages (CLI):
php bin/console messenger:consume async -vv
Use Messenger to asynchronously send emails or process background jobs (e.g., image resizing, report generation) without blocking HTTP responses.
$this->bus->dispatch($message);
AsyncBus (requires transport like Redis, AMQP, or Doctrine).
$this->bus->dispatch($message); // Handled by transport
Configure transports in config/packages/messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%' # e.g., 'doctrine://default'
failed: 'doctrine://default' # For failed messages
routing:
'App\Messages\SendEmailMessage': async
RetryStamp or configure retry delays.
use Symfony\Component\Messenger\Stamp\RetryStamp;
$message->with(new RetryStamp(3)); // Retry 3 times
failed transport:
php bin/console messenger:consume failed -vv
Use DelayStamp for scheduled execution:
use Symfony\Component\Messenger\Stamp\DelayStamp;
$message->with(new DelayStamp(3600)); // Delay 1 hour
Group messages for efficiency (e.g., bulk emails):
# config/packages/messenger.yaml
framework:
messenger:
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
batch_size: 50 # Process 50 messages at once
Add middleware to modify messages (e.g., logging, validation):
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
class LoggingMiddleware implements MiddlewareInterface
{
public function handle($message, StackInterface $next)
{
\Log::info('Handling message', ['message' => $message]);
return $next($message);
}
}
Register in config/packages/messenger.yaml:
framework:
messenger:
middleware:
- App\Middleware\LoggingMiddleware
Prevent tampering with SignatureStamp:
use Symfony\Component\Messenger\Stamp\SignatureStamp;
$message->with(new SignatureStamp('my_secret_key'));
Configure in messenger.yaml:
framework:
messenger:
signature_keys: ['my_secret_key']
Use TestMessageBus for unit tests:
use Symfony\Component\Messenger\Test\MessageBus;
$bus = new MessageBus([new TestMessageBus()]);
$bus->dispatch($message);
$enqueuedMessages = $bus->getEnqueuedMessages();
Register Messenger in AppServiceProvider:
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
public function boot()
{
$this->app->bind(MessageBus::class, function ($app) {
return new MessageBus([
new HandleMessageMiddleware($app->get('messenger.message_bus_provider')),
]);
});
}
Use Laravel’s queue system alongside Messenger:
php artisan queue:work --queue=async
Configure in config/queue.php:
'connections' => [
'async' => [
'driver' => 'messenger',
'dsn' => env('MESSENGER_TRANSPORT_DSN'),
],
],
Combine Messenger with Laravel Events:
use Symfony\Component\Messenger\MessageBusInterface;
class UserRegisteredListener
{
public function __construct(private MessageBusInterface $bus) {}
public function handle(UserRegistered $event)
{
$this->bus->dispatch(new SendWelcomeEmailMessage($event->user->email));
}
}
Use Messenger for CLI commands:
use Symfony\Component\Messenger\Attribute\AsCommand;
#[AsCommand]
class ProcessOrdersCommand
{
public function __invoke()
{
// Process orders asynchronously
}
}
Run with:
php bin/console messenger:consume commands -vv
php bin/console messenger:setup-transport doctrine://default
heartbeat and reconnect_delay to handle network issues:
options:
heartbeat: 300
reconnect_delay: 500
Serializable or use __toString() for serialization.User referencing Order which references User).#[AsMessageHandler] (PHP 8+) or @AsMessageHandler (PHP 7.4) for clarity.RetryStamp or a RetryMiddleware.failed transport manually if needed:
php bin/console messenger:failed:remove --all
batch_size: 20-50.php bin/console messenger:consume async -vv --limit=10
php bin/console messenger:consume async -vvv
Xdebug with messenger:debug:
php bin/console messenger:debug
.env:
MESSENGER_TRANSPORT_DSN=doctrine://default
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
fallback: 'sync://'
messenger:stats:
php bin/console messenger
How can I help you explore Laravel packages today?