symfony/mime
The Symfony MIME component lets you create, parse, and manipulate MIME messages in PHP, handling email bodies, attachments, headers, and encodings so you can build or inspect standards-compliant multipart messages with ease.
Installation:
composer require symfony/mime
No additional configuration is required—just autoload the package. The latest v5.4.54 release (with equivalent patches on 6.4 / 7.x / 8.x) is a security release that tightens header and address validation; see Validation and Sanitization below before upgrading.
First Use Case: Create a simple email with HTML content and an attachment:
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;
$email = (new Email())
->from('[email protected]')
->to('[email protected]')
->subject('Hello!')
->html('<h1>Hello!</h1>');
// Add an attachment
$file = new DataPart(file_get_contents('document.pdf'));
$file->filename = 'document.pdf';
$file->contentDisposition = 'attachment';
$email->attach($file);
// Send via Symfony Mailer (what Laravel >= 9 uses under the hood)
$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
'smtp.example.com', 587
);
$transport->setUsername('user');
$transport->setPassword('pass');
$mailer = new \Symfony\Component\Mailer\Mailer($transport);
$mailer->send($email);
Note: SwiftMailer is abandoned—both Symfony Mailer and Laravel >= 9 use Symfony Mime under the hood, so wire this up via
Mail::send()/Mail::to()->send($mailable)rather thannew Swift_Mailer(...).
Key Classes to Explore:
Email: For composing emails.Message: For raw MIME message manipulation.DataPart: For attachments or embedded content.File (Symfony\Component\Mime\Part\File): For detecting MIME types of files.->html() or ->text() with template variables:
$email = (new Email())
->html(view('emails.welcome', ['user' => $user]));
$email = (new Email())
->html('<h1>HTML</h1>')
->text('Plain text fallback')
->priority(Email::PRIORITY_HIGH);
// Inline (e.g., embedded images)
$image = new DataPart(file_get_contents('image.jpg'));
$image->contentId = 'logo';
$email->embed($image);
// Attachment
$file = new DataPart(file_get_contents('report.pdf'));
$file->filename = 'report.pdf';
$file->contentDisposition = 'attachment';
$email->attach($file);
Symfony\Component\Mime\Email and hand off via Mailable::withSymfonyMessage():
use Symfony\Component\Mime\Email;
class WelcomeMail extends Mailable {
public function build() {
$email = (new Email())
->from('[email protected]')
->to($this->user->email)
->subject('Welcome!')
->html($this->renderView());
return $this->view('emails.welcome')
->withSymfonyMessage($email);
}
}
use Symfony\Component\Mime\Email;
$rawEmail = file_get_contents('email.eml');
$email = Email::fromString($rawEmail);
// Access headers
$subject = $email->getSubject();
$from = $email->getFrom()[0]->getAddress();
// Access attachments
foreach ($email->getAttachments() as $attachment) {
file_put_contents("attachments/{$attachment->getFilename()}", $attachment->bodyToString());
}
use Symfony\Component\Mime\Part\File;
$file = new File('/path/to/uploaded/file.pdf');
$mimeType = $file->getMimeType(); // e.g., 'application/pdf'
use Symfony\Component\Mime\MimeTypes;
$mimeTypes = new MimeTypes();
$mimeTypes->addType('application/vnd.myformat', 'myfile');
$mimeType = $mimeTypes->guessMimeType('document.myfile');
user@[invalid], per CVE-2026-45067), the v5.4.54 security release adds:
@ in the local part of an address (e.g. user@[email protected]).@ inside a quoted local part is no longer treated as the addr-spec separator, fixing "user@external"@example.com-style addresses.Address/Headers from user input—code relying on lenient parsing will now throw.Email or use Symfony’s Validator:
use Symfony\Component\Validator\Constraints as Assert;
$email = new Email();
$email->setTo(new Address('[email protected]', 'User'));
$violations = $validator->validate($email->getTo()[0], [
new Assert\Email(),
]);
Build on Symfony Mime inside a Mailable:
class CustomMailer extends Mailable {
public function build() {
$email = (new Email())
->from(config('mail.from.address'))
->to($this->recipient)
->subject($this->subject)
->html($this->renderView());
return $this->view('emails.custom')
->withSymfonyMessage($email);
}
}
Queue Email Processing: Use Laravel queues to handle bulk emails efficiently:
class SendEmailJob implements ShouldQueue {
public function handle() {
Mail::to('[email protected]')->send(
(new Email())
->from('[email protected]')
->subject('Your Update')
->html('...')
);
}
}
File Uploads with MIME Detection: Validate uploads before storage:
use Symfony\Component\Mime\Part\File;
public function store(Request $request) {
$file = new File($request->file('document')->getPathname());
$mimeType = $file->getMimeType();
if (!str_starts_with($mimeType, 'application/pdf')) {
throw new \Exception('Invalid file type');
}
// Store file...
}
Cache MIME Type Guessers: Cache results for frequently accessed files:
$cache = new \Symfony\Component\Cache\Simple\FilesystemCache();
$file = new File('document.pdf');
$mimeType = $cache->get('mime_type_' . $file->getPathname(), function() use ($file) {
return $file->getMimeType();
});
Batch Processing: Process emails in chunks for bulk operations:
$emails = Email::fromString($rawBatch);
foreach ($emails as $email) {
// Process each email (e.g., parse, store, or forward)
}
File::getMimeType() returns incorrect results for custom file types.
$mimeTypes = new MimeTypes();
$mimeTypes->addType('application/vnd.myapp', 'myext');
$file = new File('document.myapp', null, $mimeTypes);
$mimeType = $file->getMimeType(); // Now returns 'application/vnd.myapp'
File::getMimeTypeGuesser() to inspect guessers:
$guesser = $file->getMimeTypeGuesser();
$guesser->guessMimeType('document.pdf'); // Debug individual guessers
[email protected]) may be rejected.
Address with custom validation:
use Symfony\Component\Mime\Address;
$address = new Address('[email protected]');
if (!$address->isValid()) {
throw new \InvalidArgumentException('Invalid email address');
}
Content-ID in some versions.
filename and contentDisposition:
$attachment = new DataPart(file_get_contents('file.pdf'));
$attachment->filename = 'custom_name.pdf';
$attachment->contentDisposition = 'attachment';
$email->attach($attachment);
field-name characters (printable ASCII, no :, no whitespace, no CR/LF):
$email->getHeaders()->addTextHeader('X-App-Version', '1.0'); // OK
// $email->getHeaders()->addTextHeader("X-App-Version\r\nBcc", '...'); // rejected
@ Handling in Local Parts and Domains (v5.4.54+)@ in the local part (user@[email protected]) is now rejected. Conversely, a quoted local part containing @ ("user@external"@example.com) is now parsed correctly instead of the domain being split off at the first @.@; otherwise, let validation fail fast:
new Address('"user@external"@example.com'); // valid — quoted local part
// new Address('user@[email protected]'); // rejected as of v5.4.54
toString() output instead:
$subject = preg_replace('/[\x00-\x1F\x7F]/', '', $userInput);
$email->subject($subject);
MimeTypeGuesserInterface implementations via MimeTypes::registerGuesser().How can I help you explore Laravel packages today?