lasso/mail-parser-bundle
A Symfony bundle for parsing incoming email messages into structured data, handling headers, bodies, and attachments so applications can reliably process mail without writing low-level MIME parsing code.
This is a Symfony 2-era bundle that wraps Zend\Mail / Zend\Mime so you don't have to hand-roll RFC 822 parsing. It is small, unmaintained since 2014, and has no dependents — treat it as a thin convenience layer, not a supported library.
Install and enable
composer require lasso/mail-parser-bundle
// app/AppKernel.php
$bundles = array(
// ...
new Lasso\MailParserBundle\LassoMailParserBundle(),
);
The bundle's value is one thing: turning a raw message string into a Zend\Mail\Message populated with decoded headers, a text part, an HTML part, and Zend\Mime\Part attachments.
Where to look first
Resources/config/services.yml — the service id is what you actually need. It will be something like lasso_mail_parser.parser or lasso_mail_parser.service. Read the definition before writing any code; the public method names are not documented anywhere.DependencyInjection/ — confirms whether there are configuration keys (charset, storage path for attachments) or none at all.First use case
$parser = $this->get('lasso_mail_parser.parser'); // confirm id in services.yml
$raw = file_get_contents('/path/to/message.eml');
$message = $parser->parse($raw);
echo $message->getSubject();
echo $message->getFrom();
echo $message->getBodyText(); // or getBodyHtml(), depending on the wrapper
That is the whole happy path: raw string in, message object out. Anything beyond that you build yourself on top of Zend\Mail.
Wrap it in your own service, immediately. The bundle id and method names are unstable and undocumented. Put a single facade class in your app (MailParser with parse(string $raw): ParsedMail) so that when you eventually drop the bundle for laminas/laminas-mail or zbateson/mail-mime-parser, only one file changes.
Parsing from a mail source rather than a string. In practice you rarely have a .eml on disk. Typical adapters:
imap_fetchbody($mbox, $num, '') or, better, fetch the whole RFC822 header+body (imap_fetchbody($mbox, $num, '1') is not the raw message — use imap_fetchheader + imap_body concatenated, or a library that returns the full source).^From lines, feed each chunk to the parser.Attachment handling. The bundle does not persist anything for you — it gives you parts. Loop, decode, and write to your own storage with a generated filename; never trust the filename from Content-Disposition:
foreach ($message->getParts() as $part) {
if ($part->getFileName()) {
$name = bin2hex(random_bytes(8)) . '-' . preg_replace('/[^A-Za-z0-9._-]/', '_', $part->getFileName());
file_put_contents($storageDir . '/' . $name, $part->getContent());
}
}
Processing in a console command, not a controller. Mail parsing is batch work with unpredictable payload sizes. A command (app/console mail:parse-dir /path) keeps memory and timeouts under your control and lets you re-run fixtures during development.
Fixture-driven development. Drop five .eml files into Tests/fixtures/: plain text, multipart/alternative, multipart/mixed with an attachment, a message with a non-UTF-8 charset (ISO-8859-1 and Shift_JIS are the useful ones), and one malformed message. Every parser change gets run against these. This is the single highest-value investment you can make with this bundle, because the underlying Zend code has known charset edge cases and no one is going to fix them for you.
Integration tip: normalise early. Right after parsing, convert everything to UTF-8 and your internal DTO. Do not let Zend\Mail\Message objects leak into your domain layer or get serialised into a queue — the classes are old and their serialisation is not stable.
It is abandoned, and so is its dependency. The code targets zendframework/zend-mail from the Zend Framework 2 era. That package is now laminas/laminas-mail, and zendframework/* is abandoned on Packagist. Composer may refuse to install on modern PHP, or install with a pile of advisories. Budget realistic time for a fork or replacement; do not assume you'll still be using this in a year.
PHP version ceiling. Written in 2014, so expect PHP 5.4–5.6 idioms — possibly mysql_*-era style, missing return types, no ??-safety. On PHP 8 you will hit Creating default object from empty value warnings, dynamic property deprecations, and possibly fatal signature mismatches. Check the composer.json require block first; if you're on PHP 8, assume surgery.
LGPL-3.0 is not the same as MIT. Dynamic linking via Composer is normally fine, but if you modify the bundle source and distribute your application, the modifications to the LGPL-covered files must be made available. Since this package is unmaintained, you will be modifying it. Keep your patches in a separate, published fork rather than silently vendoring a hacked copy — it keeps you compliant and makes the decision reviewable.
Charset and encoding bugs are the main failure mode. Subjects and display names arrive as RFC 2047 encoded-words; bodies arrive in whatever the sender claimed. Symptoms: mojibake in subjects, =?UTF-8?B?...?= appearing literally, truncated bodies on long lines. Debug by dumping the raw source for the failing message and inspecting the exact Content-Type / Content-Transfer-Encoding headers before blaming your code.
Bodies can be empty even when the mail clearly has content. If the message is multipart/alternative and the HTML part is the one that parsed, getBodyText() returns nothing. Always fall back: try text, then HTML, then strip tags. Decide this in your facade, once.
Memory. A 20 MB attachment becomes a 27 MB base64 string, then a decoded string in memory. If you're parsing a mailbox in a loop, unset() the message and parts each iteration and cap attachment size before decoding. A long-running worker will otherwise grow until it dies.
No tests, no issues, no maintainer. Before you invest, check whether a modern alternative fits: zbateson/mail-mime-parser (actively maintained, streaming, PSR-friendly) or laminas/laminas-mail directly. This bundle saves you maybe 40 lines of glue. If those 40 lines are the only thing you need, vendor the one class into your app instead of taking the dependency.
Extension point is the Zend object itself. If the bundle's wrapper doesn't expose something (e.g. Reply-To, custom headers), call the underlying message: $message->getHeaders()->get('Reply-To'). You don't need to patch the bundle for header access — only for parsing/normalisation bugs.
Debugging tip: log the raw message (or a hash plus size, if it may contain PII) alongside any parse failure. Because the failure is almost always source-dependent, a reproducible fixture is worth more than a stack trace.
How can I help you explore Laravel packages today?