ijanki/mailmimedecode-bundle
ijanki/mailmimedecode-bundle decodes MIME-encoded email content, helping PHP applications convert encoded headers, subjects, and message bodies into readable UTF-8 text. It simplifies handling quoted-printable and base64 mail parts so your app can display and process incoming messages reliably.
This package is a Symfony2 Bundle, not a Laravel package — there is no service provider, facade, config file, or config/app.php entry to register. Before you try to install it in a Laravel app, understand what it actually is: a thin Symfony wrapper around the legacy PEAR Mail_mimeDecode class for turning raw RFC 822 / MIME message strings into a parsed tree of headers, bodies, and attachments.
The realistic minimal steps for a Laravel developer:
composer require the bundle into a Laravel project. The bundle depends on Symfony 2 components and will drag in conflicting versions.MailMimeDecode::decode() usage and how it walks parts recursively — that is the entire value proposition, and it is ~50 lines of glue.pear/mail_mime-decode directly if you truly need a lightweight parser and can tolerate its quirks.zbateson/mail-mime-parser or php-mime-mail-parser instead, which are actively maintained and PSR-friendly.First use case: parsing an inbound email. In a Laravel app the entry point is usually a webhook payload (Postmark, Mailgun, SendGrid) or an IMAP fetch (via webklex/laravel-imap). You receive a raw MIME string and need to answer: what is the plain-text body, what is the HTML body, what attachments came with it, and what were the headers? That is exactly the job Mail_mimeDecode does — the Symfony bundle just wraps it.
Pattern 1 — Call the parser directly, skip the bundle.
If you go the Mail_mimeDecode route, install the PEAR class standalone and wrap it in a small service of your own:
namespace App\Services;
class MimeDecoder
{
public function decode(string $raw): object
{
$decoder = new \Mail_mimeDecode($raw);
$params = [
'include_bodies' => true,
'decode_bodies' => true,
'decode_headers' => true,
];
return $decoder->decode($params);
}
}
Bind it once in a MailServiceProvider (or just use the container autowiring) and inject it where needed.
Pattern 2 — Recursive part walking.
Mail_mimeDecode returns a nested parts array. The only reliable pattern is recursion — you cannot assume depth or ordering:
public function walk(object $part, array &$out): void
{
$type = $part->ctype_primary . '/' . $part->ctype_secondary;
if (!empty($part->parts)) {
foreach ($part->parts as $child) {
$this->walk($child, $out);
}
return;
}
$out[] = [
'type' => $type,
'charset' => $part->ctype_parameters['charset'] ?? null,
'disposition' => $part->disposition ?? 'inline',
'filename' => $part->d_parameters['filename'] ?? null,
'body' => $part->body ?? '',
];
}
Use this for text bodies, HTML alternates, and attachments alike.
Pattern 3 — Queue the work, not the HTTP request.
Email parsing is CPU-heavy and can be slow for large attachments. Accept the webhook, store the raw payload, and dispatch a job:
class IngestInboundEmail implements ShouldQueue
{
public function __construct(
public string $raw,
public string $messageId,
) {}
public function handle(MimeDecoder $decoder): void
{
$parsed = $decoder->decode($this->raw);
// extract text/html/attachments, persist, or dispatch further events
}
}
This decouples you from webhook timeouts and matches how Postmark/Mailgun retry.
Pattern 4 — Normalize across providers.
Different inbound providers shape the raw payload differently. Always persist the raw string (LONGTEXT), then run your decoder in one place. That way if you swap Mail_mimeDecode for zbateson/mail-mime-parser later, nothing upstream changes.
Pattern 5 — Attachment handling.
Write attachment bytes to Laravel's storage driver rather than into your database:
if (str_starts_with($part['disposition'], 'attachment') || $part['filename']) {
Storage::disk('local')->put(
"inbound/{$messageId}/{$part['filename']}",
$part['body']
);
}
Keep the attachment metadata (filename, MIME type, size) in the DB and the bytes on disk.
Integration tip: if you're using webklex/laravel-imap for polling mailboxes, $message->getRawBody() gives you exactly the string Mail_mimeDecode expects — no adapter needed.
MailMimeDecodeServiceProvider, no publishing, no facade. Anything you do in Laravel is your own wrapper around the PEAR class, not the bundle.require it.pear/mail_mime-decode itself is unmaintained. The last releases predate PHP 8. Expect deprecation notices and possible fatal errors on modern PHP. Test in a scratch project first.Mail_mimeDecode handles base64 and quoted-printable but mishandles some multi-byte encodings, RFC 2231 extended parameters (filename*=UTF-8''...), and malformed headers. Sanitize file names and re-check text bodies with mb_detect_encoding + mb_convert_encoding to UTF-8.decode_bodies => true holds the whole thing in memory. For anything over a few MB, prefer streaming parsers (zbateson/mail-mime-parser supports streams) or set decode_bodies => false and fetch the raw part yourself.parts is not always present. A single-part text/plain message has no parts key at all — only body. Guard for it.decode_headers => true mangles some encoded words (=?UTF-8?B?...?=). For strict RFC 2047 decoding, use iconv_mime_decode() on the raw header value.Mail_mimeDecode::decode() can return null on malformed input without an exception. Always null-check the return value and log the raw payload for replay.json_encode($parsed, JSON_PRETTY_PRINT) after a test decode. The ctype_primary, ctype_secondary, disposition, and d_parameters keys tell you everything about how a part was interpreted — this is the fastest way to diagnose "why did my attachment come through empty."zbateson/mail-mime-parser — actively maintained, stream-friendly, handles RFC 2231 correctly. Best default.php-mime-mail-parser/php-mime-mail-parser — also maintained, built on mailparse extension, very fast.MimeDecoderContract) into your job and binding either the PEAR implementation or a zbateson implementation — that's your migration path without touching the rest of your codebase.Bottom line for a Laravel developer: treat this repo as a reference implementation of the pattern, not a dependency. Write a 20-line MimeDecoder service, back it with zbateson/mail-mime-parser, queue the parsing, and store raw + parts separately. You'll get 90% of the value with none of the PEAR-era baggage.
How can I help you explore Laravel packages today?