wp-spaghetti / wp-mail-transport
Symfony Mailer transport for WordPress wp_mail() - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations
Fund package maintenance!
buymeacoff.ee/frugan
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/wp-spaghetti/wp-mail-transport
Requires
- php: >=8.2
- illuminate/support: ^10.0 || ^11.0 || ^12.0
- symfony/mailer: ^6.0 || ^7.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.48
- laravel/pint: ^1.0
- phpunit/phpunit: ^11.0
README
WP Mail Transport
Symfony Mailer transport that uses WordPress's wp_mail() function, allowing you to use Laravel's Mail facade while leveraging WordPress email plugins.
Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.
Features
- Clean Laravel Syntax - Use
Mail::to(),Mail::send(), etc. anywhere in your code - WordPress Plugin Support - Works seamlessly with WP Mail SMTP, SendGrid, Mailgun, and other WP email plugins
- Zero Configuration - Works out of the box with sensible defaults
- Framework Agnostic - Not tied to Acorn, works across different Laravel+WordPress stacks
- Full Symfony Mailer API - Support for attachments, HTML emails, CC, BCC, custom headers
Requirements
- PHP >= 8.2
- WordPress >= 6.0
- Laravel Illuminate/Support ^10.0|^11.0|^12.0
- Symfony Mailer ^6.0|^7.0
Installation
1. Install the package
In your Laravel + WordPress project (Sage theme, WP Starter, etc.):
composer require wp-spaghetti/wp-mail-transport
The package auto-registers via service provider discovery.
2. Configure mail transport
Update your config/mail.php:
<?php return [ 'default' => env('MAIL_MAILER', 'wp-mail'), 'mailers' => [ 'wp-mail' => [ 'transport' => 'wp-mail', ], // Keep other transports for fallback 'smtp' => [ 'transport' => 'smtp', 'host' => env('MAIL_HOST', '127.0.0.1'), 'port' => env('MAIL_PORT', 2525), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), ], ], 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 'name' => env('MAIL_FROM_NAME', 'Example'), ], ];
3. Set environment variable
Update your .env:
MAIL_MAILER=wp-mail
That's it! The transport will now use WordPress's wp_mail() function.
Usage
Basic Email
use Illuminate\Support\Facades\Mail; Mail::raw('Email body content', function ($message) { $message->to('user@example.com') ->subject('Test Email'); });
Using Mailables
use Illuminate\Support\Facades\Mail; use App\Mail\WelcomeEmail; Mail::to($user->email)->send(new WelcomeEmail($user));
HTML Emails
Mail::send('emails.welcome', ['user' => $user], function ($message) use ($user) { $message->to($user->email) ->subject('Welcome to our application'); });
With Attachments
Mail::send('emails.invoice', $data, function ($message) use ($user, $pdf) { $message->to($user->email) ->subject('Your Invoice') ->attach($pdf); });
Multiple Recipients
Mail::to('user1@example.com') ->cc('user2@example.com') ->bcc('admin@example.com') ->send(new OrderShipped($order));
Custom Headers
Mail::raw('Email body', function ($message) { $message->to('user@example.com') ->subject('Test') ->getHeaders() ->addTextHeader('X-Custom-Header', 'value'); });
Framework-Specific Examples
Sage Themes (Acorn)
// In app/Controllers/ContactController.php use Illuminate\Support\Facades\Mail; public function submit(Request $request) { Mail::to('info@example.com')->send( new ContactFormSubmission($request->all()) ); return back()->with('success', 'Message sent!'); }
WP Starter
// In your custom plugins or theme use Illuminate\Support\Facades\Mail; add_action('user_register', function($user_id) { $user = get_userdata($user_id); Mail::to($user->user_email)->send( new WelcomeEmail($user) ); });
Corcel
use Corcel\Model\User; use Illuminate\Support\Facades\Mail; $users = User::published()->get(); foreach ($users as $user) { Mail::to($user->user_email)->send( new NewsletterEmail($user) ); }
How It Works
Architecture
Laravel Mail::to()->send()
↓
Symfony Mailer
↓
WpMailTransport
↓
wp_mail()
↓
WordPress Email Plugins
├─ WP Mail SMTP
├─ SendGrid
├─ Mailgun
├─ Amazon SES
└─ Other plugins
↓
Email Delivery
Technical Details
Email Conversion:
- Converts Symfony Message to Email object
- Extracts headers, recipients, subject, body
- Handles HTML and plain text bodies
- Processes attachments
Header Handling:
- Filters out headers that
wp_mail()adds automatically (From,To,Subject,Content-Type) - Preserves custom headers (CC, BCC, Reply-To, X-* headers)
- Properly formats From header with name and address
Attachment Processing:
- Creates temporary files for attachments
- Passes file paths to
wp_mail() - Cleans up temporary files after sending
Error Handling:
- Throws
TransportExceptionif sending fails - Proper exception chaining for debugging
- Graceful cleanup on errors
WordPress Plugin Integration
The transport automatically works with popular WordPress email plugins:
WP Mail SMTP
Configure WP Mail SMTP in WordPress admin, then use Laravel's Mail facade normally:
Mail::to('user@example.com')->send(new OrderConfirmation($order)); // Uses WP Mail SMTP configuration automatically
SendGrid Plugin
Install and configure the SendGrid WordPress plugin:
Mail::to($user->email)->send(new WelcomeEmail($user)); // Sends through SendGrid via WordPress plugin
Mailgun Plugin
Configure Mailgun in WordPress, then:
Mail::to($subscribers)->send(new Newsletter($content)); // Routes through Mailgun WordPress plugin
Advanced Configuration
Publish Configuration (Optional)
# Sage/Acorn wp acorn vendor:publish --tag=wp-mail-config # WP Starter (using Laravel's artisan) php vendor/bin/wp-starter vendor:publish --tag=wp-mail-config
This creates config/wp-mail.php with debugging options:
<?php return [ 'debug' => env('WP_MAIL_DEBUG', false), ];
Debug Mode
Enable debug logging to troubleshoot email delivery issues:
In .env:
WP_MAIL_DEBUG=true
Or in config/wp-mail.php:
'debug' => true,
What gets logged:
- Email recipients
- Subject line
- Content type (HTML or plain text)
- Number of headers
- Number of attachments
- wp_mail() success/failure status
- Exception details if sending fails
Logging System:
Debug messages are logged using Laravel's Log facade at the debug level. Configure logging in config/logging.php:
// Log to daily files 'channels' => [ 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', // Make sure debug level is included 'days' => 14, ], ],
Log Format: Laravel automatically formats logs with timestamp and context:
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Sending email via wp_mail() {"to":["user@example.com"],"subject":"Order Confirmation","content_type":"text/html","headers_count":2,"attachments_count":1}
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Email sent successfully via wp_mail()
Log Channels:
You can specify different log channels in config/logging.php:
single- Single filedaily- Daily rotating filesslack- Send to Slacksyslog- System logerrorlog- PHP error logstack- Multiple channels
Note: Debug mode logs sensitive information (email addresses). Only enable in development or when troubleshooting specific issues.
Multiple Mailers
You can use multiple transports in the same application:
// config/mail.php 'mailers' => [ 'wp-mail' => [ 'transport' => 'wp-mail', ], 'smtp' => [ 'transport' => 'smtp', // ... SMTP config ], ],
Then switch between them:
// Use WP Mail transport (default) Mail::to($user)->send(new Welcome($user)); // Use SMTP directly Mail::mailer('smtp')->to($admin)->send(new Alert($data));
Custom From Address
Set globally in config/mail.php:
'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'noreply@example.com'), 'name' => env('MAIL_FROM_NAME', 'My Application'), ],
Or per-email:
Mail::to($user) ->from('custom@example.com', 'Custom Sender') ->send(new CustomEmail());
Troubleshooting
Emails not being sent
- Check if wp_mail() is working:
$result = wp_mail('test@example.com', 'Test', 'Test message'); var_dump($result); // Should be true
-
Check WordPress email plugins:
- Ensure WP Mail SMTP or similar plugin is configured
- Test sending from WordPress admin
-
Enable debug mode:
WP_MAIL_DEBUG=true LOG_LEVEL=debug
With debug mode enabled, the transport will log detailed information about each email using Laravel's logging system. Check your logs in storage/logs/laravel.log:
What gets logged:
- Recipients
- Subject
- Content type (HTML/plain text)
- Number of headers
- Number of attachments
- Success/failure status
Example debug output:
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Sending email via wp_mail() {"to":["user@example.com"],"subject":"Welcome","content_type":"text/html","headers_count":3,"attachments_count":0}
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Email sent successfully via wp_mail()
Configure logging channels in config/logging.php to send logs to different destinations (files, Slack, syslog, etc.).
Attachments not working
Ensure your WordPress upload directory is writable:
$upload_dir = wp_upload_dir(); echo $upload_dir['basedir']; // Should be writable
Custom headers not appearing
Some WordPress email plugins may strip custom headers. Check your plugin's settings or filters.
From address being overridden
WordPress plugins may force a specific From address. Check:
// In your WordPress code add_filter('wp_mail_from', function($email) { return 'your-desired@example.com'; });
Testing
composer test
Comparison with Other Solutions
| Feature | wp-mail-transport | roots/acorn-mail | Others |
|---|---|---|---|
| Framework Support | ✅ Acorn, WP Starter, Corcel, Custom | ❌ Acorn only | ❌ Usually Acorn only |
| WordPress Plugin Integration | ✅ Yes | ❌ No (SMTP only) | ⚠️ Limited |
| Configuration Required | ✅ Minimal | ⚠️ SMTP setup needed | ⚠️ Varies |
| Attachment Support | ✅ Yes | ✅ Yes | ✅ Yes |
| HTML Email Support | ✅ Yes | ✅ Yes | ✅ Yes |
| Maintained | ✅ Active | ✅ Active | ⚠️ Varies |
Changelog
Please see CHANGELOG for a detailed list of changes for each release.
We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.
Release Process
- Major versions (1.0.0 → 2.0.0): Breaking changes
- Minor versions (1.0.0 → 1.1.0): New features, backward compatible
- Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible
All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.
Contributing
For your contributions please use:
See CONTRIBUTING for detailed guidelines.