Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Messenger Laravel Package

symfony/messenger

Symfony Messenger helps PHP apps send and handle messages asynchronously via queues or between services. It provides message buses, handlers, transports, retries, and failure handling to build reliable background jobs and event-driven workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation (updated for v8.1.0-BETA3):

    composer require symfony/messenger:^8.1.0-BETA3
    

    For Laravel, use symfony/messenger alongside symfony/amqp-messenger (RabbitMQ) or symfony/redis-messenger (Redis) as needed.

  2. Define a Message Class (unchanged):

    namespace App\Messages;
    
    class SendEmailMessage
    {
        public function __construct(public string $email, public string $subject, public string $body) {}
    }
    
  3. Create a Message Handler (updated for retry strategy):

    namespace App\MessageHandlers;
    
    use App\Messages\SendEmailMessage;
    use Symfony\Component\Messenger\Attribute\AsMessageHandler;
    use Symfony\Component\Messenger\Exception\RecoverableMessageHandlingException;
    
    #[AsMessageHandler]
    class SendEmailHandler
    {
        public function __invoke(SendEmailMessage $message)
        {
            try {
                // Logic to send email
            } catch (\Exception $e) {
                throw new RecoverableMessageHandlingException($e->getMessage(), $e->getCode(), $e);
            }
        }
    }
    
  4. Dispatch a Message (unchanged):

    use Symfony\Component\Messenger\MessageBusInterface;
    
    public function __construct(private MessageBusInterface $bus) {}
    
    public function sendWelcomeEmail(string $email)
    {
        $this->bus->dispatch(new SendEmailMessage(
            $email,
            'Welcome!',
            'Thanks for signing up!'
        ));
    }
    
  5. Configure a Transport (updated for signing):

    return [
        'transports' => [
            'async' => [
                'dsn' => 'sync://',
                'options' => [
                    'signing_key' => '%env(MESSENGER_SIGNING_KEY)%',
                ],
            ],
            'failed' => [
                'dsn' => 'doctrine://default?queue_name=failed',
            ],
        ],
        'routing' => [
            SendEmailMessage::class => 'async',
        ],
        'serializer' => [
            'default' => 'messenger.transport.serializer',
            'default_options' => [
                'sign_messages' => true,
            ],
        ],
    ];
    
  6. Run the Worker (unchanged):

    php artisan messenger:consume async -vv
    

First Use Case: Async Email Processing with Retry Strategy (updated)

// In UserController
public function register(Request $request)
{
    $user = User::create($request->validated());
    $this->bus->dispatch(new SendEmailMessage(
        $user->email,
        'Welcome!',
        'Thanks for signing up!'
    ));
    return response()->json(['message' => 'User created!']);
}

// In SendEmailHandler (updated)
public function __invoke(SendEmailMessage $message)
{
    try {
        Mail::to($message->email)
            ->send(new WelcomeEmail($message->subject, $message->body));
    } catch (MailerException $e) {
        throw new RecoverableMessageHandlingException(
            'Failed to send email: ' . $e->getMessage(),
            0,
            $e
        );
    }
}

Implementation Patterns

1. Message Dispatching (updated)

  • Synchronous vs. Async (unchanged)
  • Delayed Messages (unchanged)
  • Routing (unchanged)
  • Signed Messages (new): Enable signing in config to ensure message integrity. Use sign_messages: true in serializer options and provide a signing key.
    'serializer' => [
        'default_options' => [
            'sign_messages' => true,
        ],
    ],
    

2. Handling Messages (updated)

  • Handlers (updated): Use RecoverableMessageHandlingException for failures that should trigger retries (e.g., transient network issues). Non-recoverable failures should throw generic exceptions.

    throw new RecoverableMessageHandlingException('Transient error', 0, $e);
    
  • Middleware (unchanged)

  • Batch Processing (unchanged)

3. Transports and Failure Handling (updated)

  • Transport Config (updated): Configure signing for transports requiring message integrity checks.

    'async' => [
        'dsn' => 'amqp://guest:guest@localhost:5672/%2f/messages',
        'options' => [
            'signing_key' => '%env(MESSENGER_SIGNING_KEY)%',
        ],
    ],
    
  • Retry Logic (updated): Retry strategies now respect RecoverableMessageHandlingException. Configure retry limits in retry_strategy:

    'retry_strategy' => [
        'max_retries' => 3,
        'delay' => 1000,
        'multiplier' => 2,
        'max_delay' => 0,
    ],
    
  • Failure Transport (unchanged)

4. Worker Management (unchanged)

  • Commands (unchanged)
  • Supervisor Setup (unchanged)

5. Testing (updated)

  • Signed Messages: Verify message signing in tests by inspecting the SigningStamp:

    $message->last(Symfony\Component\Messenger\Stamp\SigningStamp::class)->signature;
    
  • Retry Behavior: Mock RecoverableMessageHandlingException to test retry logic:

    $this->expectException(RecoverableMessageHandlingException::class);
    
  • Serializable Instances (new): Ensure PhpSerializer correctly handles Serializable instances by testing with custom serializable message classes:

    class SerializableMessage implements Serializable
    {
        public function serialize(): string {}
        public function unserialize(string $serialized): void {}
    }
    

Gotchas and Tips

1. Common Pitfalls (updated)

  • Message Serialization (updated): Ensure sign_messages is set to true in serializer options if using signed transports. Without signing, messages may be rejected by strict transports.

    'serializer' => [
        'default_options' => [
            'sign_messages' => true,
        ],
    ],
    
  • Transport Locks (unchanged)

  • Failed Messages (unchanged)

  • Worker Timeouts (unchanged)

  • Retry Strategy Misuse (unchanged): Avoid throwing RecoverableMessageHandlingException for non-retryable failures (e.g., invalid data).

  • Serializable Instances (new): Ensure message classes implementing Serializable are correctly handled by PhpSerializer. Test with custom serializable classes to avoid serialization errors.

2. Debugging Tips (updated)

  • Logging (updated): Enable debug logging for signing issues:

    'logging' => [
        'enabled' => true,
        'level' => 'debug',
    ],
    
  • Message Inspection (updated): Inspect signed messages for integrity:

    php artisan messenger:failed:list --format=json | grep "signature"
    
  • Transport-Specific Issues (updated):

    • AMQP/RabbitMQ: Validate signing keys in transport options. Corrupted keys may cause message rejection.
    • Doctrine: Ensure the sign_messages option is consistent across all transports to avoid serialization mismatches.
  • PhpSerializer Fixes (new): If using PhpSerializer with Serializable instances, verify the payload structure after deserialization. The fix in #64261 ensures proper handling of such cases.

3. New Fixes and Improvements (updated)

  • Signing Security (unchanged): SigningSerializer verifies signatures before decoding messages.

  • Retry Strategy Respect (unchanged): RecoverableMessageHandlingException properly triggers retry logic.

  • Exception Normalization (unchanged): Trace arguments are no longer included in flattened exceptions.

  • Configuration Clarity (unchanged): Explicitly document the need for sign_messages: true in serializer options.

  • PhpSerializer Fix (new): Fixes PhpSerializer::getMessageType() when handling payloads with Serializable instances (#64261).

  • General Hardening (new): Various fixes and hardenings to improve stability and security (#64237). Ensure your application is updated to avoid potential edge-case issues.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle