mail-mime-parser reads RFC 822 messages in PHP without the imap functions
An email parser written in PHP
At a glance
- What is it?
- mail-mime-parser is a PHP library that parses Internet Message Format mail into a message object with typed headers, attachments and text or HTML bodies, and it deliberately replaces PHP's imap functions and the PEAR mail libraries. It suits an application that has to read raw mail from a pipe or a file, and it needs PHP 8.1 or newer.
- Who is it for?
- Adopt mail-mime-parser if your PHP application reads raw mail from a mailbox, a pipe or an upload and you want a message object rather than a bag of strings, because it returns typed header objects, decoded attachment streams and both text and HTML bodies without requiring an IMAP server or the imap extension.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why not the imap functions
mail-mime-parser is described in the README as a testable and PSR-compliant mail mime parser alternative to PHP's imap functions and Pear libraries for reading messages in Internet Message Format, RFC 822 and its later revisions RFC 2822 and RFC 5322. The positioning is the point. The imap functions are not only a parser, they are a client library that expects a live IMAP server, which means a test suite that needs a mail server, a deployment that needs IMAP credentials, and a parser whose behaviour is entangled with the server protocol. This library takes the raw message and nothing else. The project states three goals, and they are worth quoting in spirit because they explain the design: well written, standards-compliant but forgiving, and tested where possible. Forgiving is the operative word for anyone who has processed real mail, since real messages arrive with malformed headers, mislabelled encodings and boundary markers that do not match, and a parser that throws on those is a parser that will page you at three in the morning. The project is BSD licensed, the repository is not archived, and the last push was on 2026-09-28, the same day as both a 4.0.6 and a 3.0.9 release.
Installing and parsing your first message
The package is installed with composer, and the README gives the command.
composer require zbateson/mail-mime-parserThe library requires PHP 8.1 or newer and is tested on PHP 8.1, 8.2, 8.3, 8.4 and 8.5. Version 4.0 focused on API cleanup and improved configurability, with the details in the 4.0 Upgrade Guide, and there are separate upgrade guides for 1.x, 2.x, 3.x and 4.x.
There are two ways to parse. The README shows the object oriented route first, where an instance of MailMimeParser is used as a class dependency, and parse() accepts a string, a resource or a Psr7 StreamInterface.
use ZBateson\MailMimeParser\MailMimeParser;
use ZBateson\MailMimeParser\Message;
use ZBateson\MailMimeParser\Header\HeaderConsts;
// use an instance of MailMimeParser as a class dependency
$mailParser = new MailMimeParser();
// parse() accepts a string, resource or Psr7 StreamInterface
// pass `true` as the second argument to attach the passed $handle and close
// it when the returned IMessage is destroyed.
$handle = fopen('file.mime', 'r');
$message = $mailParser->parse($handle, false); // returns `IMessage`The second argument matters more than it looks. Passing false leaves the handle open, and the README closes it explicitly only when the message is no longer being used, while passing true attaches the handle so it is closed when the returned IMessage is destroyed. That is a small design decision with a real consequence: a long running worker that parses many messages and forgets to close handles leaks file descriptors, and the library will not save you from it.
The procedural entry point and Message::from
If you do not want to manage a parser instance, the README offers a procedural route, and Message::from also accepts a string, a resource or a Psr7 StreamInterface. The README notes that true or false as the second parameter does not matter if you are passing a string, since there is no handle to own in that case.
$string = "Content-Type: text/plain\r\nSubject: Test\r\n\r\nMessage";
$message = Message::from($string, false);The choice between the two is mostly a matter of how your application is structured. The parser instance is the form to use when you inject dependencies, because a parser may carry configuration and the instance can be shared. Message::from is the form to use in a script or a small utility where the configuration does not matter. The repository layout suggests the configuration options are part of the 4.0 improvements, since the version note calls out improved configurability, and the API reference for 4.0 is linked from the README.
Typed headers instead of raw strings
The API that makes this different from a header string parser is that headers come back as objects with their own methods. The README's example reads a header value, then reaches into the parsed header object to get a person name from an AddressHeader.
echo $message->getHeaderValue(HeaderConsts::FROM); // [email protected]
echo $message
->getHeader(HeaderConsts::FROM) // AddressHeader
->getPersonName(); // Person Name
echo $message->getSubject(); // The email's subjectThe To and Cc headers are also AddressHeader instances, and each holds a list of AddressPart objects with their own getPersonName and getEmail accessors. That structure is what lets you distinguish a display name from an address without writing a regular expression for the quoted string and angle bracket cases that RFC 5322 allows. Custom or undocumented headers are read with the plain getHeader call, and the library also exposes the message subject, the text body and the HTML body, with getTextContent and getHtmlContent as the two accessors.
echo $message->getTextContent(); // or getHtmlContent()
echo $message->getHeader('X-Foo'); // for custom or undocumented headersAttachments, decoded content and saving streams
Attachments are addressed by index, and the attachment part gives you its content type, the parameters of that content type such as the charset, the decoded content and a content stream that is decoded automatically.
$att = $message->getAttachmentPart(0); // first attachment
echo $att->getHeaderValue(HeaderConsts::CONTENT_TYPE); // e.g. "text/plain"
echo $att->getHeaderParameter( // value of "charset" part
HeaderConsts::CONTENT_TYPE,
'charset'
);
echo $att->getContent(); // get the attached file's contents
$stream = $att->getContentStream(); // the file is decoded automaticallyThe distinction between getContent and getContentStream is the useful one for large attachments, since the stream form lets you copy bytes without holding the whole file in memory. For the common case, the attachment can write itself out, either to a path or into another stream.
$dest = \GuzzleHttp\Psr7\stream_for(
fopen('my-file.ext')
);
\GuzzleHttp\Psr7\copy_to_stream(
$stream, $dest
);
// OR: more simply if saving or copying to another stream
$att->saveContent('my-file.ext'); // writes to my-file.ext
$att->saveContent($stream); // copies to the streamThe README's example uses Guzzle's PSR-7 utilities to copy the stream, which shows that the library's output is a standard PSR-7 stream rather than a proprietary handle, and that it composes with the HTTP client stack most PHP applications already have.
S/MIME and PGP/MIME through optional companion packages
Encryption and signing are handled by separate packages rather than by the core library, and the README is explicit that they are optional companions. There is zbateson/mmp-crypt-smime for S/MIME via PHP's OpenSSL extension, and zbateson/mmp-crypt-gpg for PGP/MIME via PEAR's Crypt_GPG. Install either package and encrypted or signed messages are automatically detected and decrypted during parsing, with the Usage Guide showing examples for reading encrypted messages and composing signed or encrypted ones. The word automatically is the important part: once the plugin is installed, the calling code does not need to branch on whether a message happens to be encrypted, which is the difference between this design and a typical decrypt-if-encrypted wrapper that every caller has to remember to write. The dependency cost is worth noting though, since the S/MIME route needs the OpenSSL extension present in your PHP build and the PGP/MIME route needs a PEAR package installed alongside.
Where mail-mime-parser is the wrong tool
There are four honest limits. The first is scope: this is a parser, not a mail client. It does not connect to a mailbox, it does not list folders, it does not mark messages as seen and it does not fetch. If your problem is fetching from an IMAP server, the imap extension or a dedicated IMAP client library is the right tool and mail-mime-parser takes over only once you have the raw bytes in hand. The second is the PHP version floor. PHP 8.1 or newer is required, so an application pinned to PHP 7.x cannot adopt the 4.x line at all, and the README provides upgrade guides going back to 1.x which tells you how much churn the API has seen across majors. The third is that it does not send mail. There is no composer function here, no SMTP transport and no DKIM handling; this is inbound only. The fourth is that a parser is only as good as the input, and a forgiving parser that accepts malformed mail can also accept mail that a stricter parser would have rejected, so a security sensitive pipeline should not assume the library's tolerance means a message is safe, and should do its own checks on what it receives. The realistic alternative to weigh is doing it by hand with PHP's own mail functions plus a MIME library of your own choosing, and for a small project with simple plain text messages that is sometimes the shorter path, while for anything with attachments, internationalised headers or multiple encodings the hand rolled route is where the bugs live.
Editorial conclusion
Adopt mail-mime-parser if your PHP application reads raw mail from a mailbox, a pipe or an upload and you want a message object rather than a bag of strings, because it returns typed header objects, decoded attachment streams and both text and HTML bodies without requiring an IMAP server or the imap extension. Do not adopt it if you need to talk to a live IMAP server to fetch mail, since that is fetching rather than parsing and the imap extension still does that job, and do not adopt it if you are on PHP 8.0 or older. Verify two things before you upgrade: which major version you are on, since the 4.0 upgrade guide describes API cleanup and improved configurability and 4.0.6 sits alongside a 3.0.9 release from the same day, and whether your environment can run the optional encryption plugins, since S/MIME support needs PHP's OpenSSL extension and PGP/MIME support needs PEAR's Crypt_GPG. The practical first step is to composer require zbateson/mail-mime-parser, parse one message from a string with Message::from and check the subject and from header before you point it at real traffic.
Frequently asked questions
What is zbateson/mail-mime-parser?
It is a PHP library that parses Internet Message Format mail, RFC 822 and its later revisions, into a message object with typed headers, attachments and text and HTML bodies. It is positioned as a testable and PSR-compliant alternative to PHP's imap functions and the PEAR mail libraries, and it is BSD licensed.
How do I install mail-mime-parser and parse a message?
Run composer require zbateson/mail-mime-parser, then create a MailMimeParser and call parse() with a string, a resource or a Psr7 StreamInterface. The second argument controls handle ownership, and the README notes it does not matter when you pass a string.
Which PHP versions does mail-mime-parser support?
It requires PHP 8.1 or newer, and the README says it is tested on PHP 8.1, 8.2, 8.3, 8.4 and 8.5. Version 4.0 focused on API cleanup and improved configurability, and the project publishes upgrade guides for the 1.x through 4.x lines.
How does mail-mime-parser handle encrypted messages?
Encryption is provided by optional companion packages, zbateson/mmp-crypt-smime for S/MIME via PHP's OpenSSL extension and zbateson/mmp-crypt-gpg for PGP/MIME via PEAR's Crypt_GPG. Once installed, encrypted and signed messages are detected and decrypted during parsing.
How do I read a header value and an attachment in mail-mime-parser?
getHeaderValue returns the plain value while getHeader returns a typed header object, such as an AddressHeader with getAddresses and getPersonName. For attachments, getAttachmentPart gives a part with getContentType related accessors, getContent for the decoded bytes, getContentStream for a decoded stream, and saveContent to write to a path or copy into another stream.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zbateson-mail-mime-parser)Was this useful?