# zbateson/mail-mime-parser ## Introduction zbateson/mail-mime-parser is a PSR-compliant PHP library for parsing MIME email messages in Internet Message Format (RFC 822, RFC 2822, RFC 5322). It provides a modern, testable alternative to PHP's imap* functions and legacy Pear libraries. The library handles the complete parsing and manipulation of email messages, including headers, multipart content, attachments, and various encoding schemes (quoted-printable, base64, UUEncode). Built with PHP 8.0+, the library uses PHP-DI for dependency injection, Guzzle PSR-7 streams for efficient content handling, and Upstash for vector embeddings support. It provides both procedural and object-oriented APIs with comprehensive error handling, validation, and logging capabilities. The parser is standards-compliant yet forgiving, handling malformed emails gracefully while maintaining strict adherence to RFC specifications. ## Core APIs and Functions ### Parsing a MIME message from file handle Parse email messages from file handles, strings, or PSR-7 streams with automatic resource management and charset detection. ```php parse($handle, true); // Access basic headers echo $message->getSubject() . "\n"; echo $message->getHeaderValue(HeaderConsts::FROM) . "\n"; echo $message->getHeaderValue(HeaderConsts::TO) . "\n"; echo $message->getHeaderValue(HeaderConsts::DATE) . "\n"; // Get message content echo $message->getTextContent(); // Plain text version echo $message->getHtmlContent(); // HTML version // Output: // Meeting Tomorrow // sender@example.com // recipient@example.com // Mon, 14 Oct 2025 10:30:00 +0000 // Let's meet at 3pm... ``` ### Parsing a MIME message from string Static factory method for quick parsing without instantiating MailMimeParser explicitly. ```php getSubject() . "\n"; echo $message->getHeaderValue(HeaderConsts::FROM) . "\n"; echo $message->getTextContent(); // Output: // Test Email // alice@example.com // Hello Bob, // // This is a test message. ``` ### Extracting sender and recipient information Access detailed address information including names and email addresses from address headers. ```php \r\n" . "To: Bob Jones , charlie@example.com\r\n" . "Cc: Diana Wilson \r\n" . "Subject: Team Update\r\n" . "\r\n" . "Message body here.\r\n"; $message = Message::from($rawEmail, false); // Get FROM header details $fromHeader = $message->getHeader(HeaderConsts::FROM); echo "From Email: " . $fromHeader->getEmail() . "\n"; echo "From Name: " . $fromHeader->getPersonName() . "\n"; // Get all TO addresses $toHeader = $message->getHeader(HeaderConsts::TO); foreach ($toHeader->getAddresses() as $address) { echo "To: " . $address->getEmail(); if ($address->getName()) { echo " (" . $address->getName() . ")"; } echo "\n"; } // Get CC addresses $ccHeader = $message->getHeader(HeaderConsts::CC); if ($ccHeader) { foreach ($ccHeader->getAddresses() as $address) { echo "Cc: " . $address->getEmail() . " (" . $address->getName() . ")\n"; } } // Output: // From Email: alice@example.com // From Name: Alice Smith // To: bob@example.com (Bob Jones) // To: charlie@example.com // Cc: diana@example.com (Diana Wilson) ``` ### Reading attachments Access, enumerate, and save email attachments with automatic decoding of content-transfer-encoding. ```php getAttachmentCount(); echo "Found {$attachmentCount} attachment(s)\n"; // Iterate through all attachments for ($i = 0; $i < $attachmentCount; $i++) { $attachment = $message->getAttachmentPart($i); // Get attachment metadata $filename = $attachment->getFilename(); $mimeType = $attachment->getContentType(); $size = $attachment->getBinaryContentStream()->getSize(); echo "Attachment {$i}: {$filename}\n"; echo " Type: {$mimeType}\n"; echo " Size: {$size} bytes\n"; // Save to file (automatically decodes base64/quoted-printable) $attachment->saveContent("/tmp/attachments/{$filename}"); // Or get content as string $content = $attachment->getContent(); // Or copy to stream $stream = $attachment->getContentStream(); $destHandle = fopen("/tmp/copy-{$filename}", 'w'); while (!$stream->eof()) { fwrite($destHandle, $stream->read(8192)); } fclose($destHandle); } // Output: // Found 2 attachment(s) // Attachment 0: document.pdf // Type: application/pdf // Size: 245760 bytes // Attachment 1: image.jpg // Type: image/jpeg // Size: 102400 bytes ``` ### Working with multipart messages Navigate and extract content from multipart/alternative and multipart/mixed messages. ```php

HTML version of the email.

\r\n" . "--boundary123--\r\n"; $message = Message::from($rawEmail, false); // Check if message is multipart if ($message->isMultiPart()) { echo "This is a multipart message\n"; } // Get text part count $textPartCount = $message->getTextPartCount(); $htmlPartCount = $message->getHtmlPartCount(); echo "Text parts: {$textPartCount}, HTML parts: {$htmlPartCount}\n"; // Access specific parts $textPart = $message->getTextPart(0); echo "Plain text:\n" . $textPart->getContent() . "\n\n"; $htmlPart = $message->getHtmlPart(0); echo "HTML:\n" . $htmlPart->getContent() . "\n"; // Get charset information echo "Text charset: " . $textPart->getCharset() . "\n"; // Output: // This is a multipart message // Text parts: 1, HTML parts: 1 // Plain text: // Plain text version of the email. // // HTML: //

HTML version of the email.

// Text charset: UTF-8 ``` ### Creating and modifying messages Build new messages or modify existing ones with text/html content and attachments. ```php setRawHeader(HeaderConsts::FROM, 'sender@example.com'); $message->setRawHeader(HeaderConsts::TO, 'recipient@example.com'); $message->setRawHeader(HeaderConsts::SUBJECT, 'Welcome Email'); // Set text and HTML content $message->setTextPart('Welcome! This is the plain text version.'); $message->setHtmlPart('

Welcome!

This is the HTML version.

'); // Add an attachment from file $message->addAttachmentPartFromFile( '/path/to/document.pdf', 'application/pdf', 'welcome-guide.pdf', 'attachment', 'base64' ); // Add an attachment from string/resource $csvData = "Name,Email\nJohn,john@example.com\n"; $message->addAttachmentPart( $csvData, 'text/csv', 'contacts.csv', 'attachment', 'base64' ); // Save complete message to file $message->save('/tmp/new-message.eml'); // Or get as string $emailString = (string)$message; // Modify existing message - remove HTML part $existingMessage = Message::from($emailString, false); $existingMessage->removeHtmlPart(0); $existingMessage->setTextPart('Updated text content only.'); echo "Message created and modified successfully\n"; ``` ### Working with signed messages Handle multipart/signed messages for S/MIME and PGP signatures. ```php setRawHeader('From', 'alice@example.com'); $message->setRawHeader('To', 'bob@example.com'); $message->setRawHeader('Subject', 'Signed Message'); $message->setTextPart('This message will be signed.'); // Convert to multipart/signed format $message->setAsMultipartSigned('sha256', 'application/pgp-signature'); // Get normalized content to sign $contentToSign = $message->getSignedMessageAsString(); // Generate signature (example using external signing) // In real usage, use openssl or gpg functions $signature = "-----BEGIN PGP SIGNATURE-----\n" . base64_encode(hash('sha256', $contentToSign, true)) . "\n-----END PGP SIGNATURE-----"; // Attach the signature $message->setSignature($signature); // Save signed message $message->save('/tmp/signed-message.eml'); // Later, verify the signature $signedMessage = Message::from(fopen('/tmp/signed-message.eml', 'r'), true); $signaturePart = $signedMessage->getSignaturePart(); if ($signaturePart) { echo "Signature found:\n"; echo $signaturePart->getContent() . "\n"; // Get signed content for verification $signedContent = $signedMessage->getSignedMessageAsString(); // Verify signature (implementation depends on signature type) echo "Content to verify hash: " . hash('sha256', $signedContent) . "\n"; } // Output: // Signature found: // -----BEGIN PGP SIGNATURE----- // [base64 encoded signature] // -----END PGP SIGNATURE----- // Content to verify hash: [hash value] ``` ### Advanced header manipulation Access and modify various header types including custom headers and parameters. ```php setRawHeader(HeaderConsts::FROM, 'alice@example.com'); $message->setRawHeader(HeaderConsts::SUBJECT, 'Test Email'); $message->setRawHeader(HeaderConsts::DATE, date('r')); // Set custom headers $message->setRawHeader('X-Mailer', 'MyMailer 1.0'); $message->setRawHeader('X-Priority', '1'); // Add multiple headers with same name $message->addRawHeader(HeaderConsts::RECEIVED, 'from mx1.example.com by mx2.example.com'); $message->addRawHeader(HeaderConsts::RECEIVED, 'from mx0.example.com by mx1.example.com'); // Get Content-Type with parameters $message->setRawHeader( HeaderConsts::CONTENT_TYPE, 'text/plain; charset=ISO-8859-1; format=flowed' ); $contentType = $message->getHeaderValue(HeaderConsts::CONTENT_TYPE); $charset = $message->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'charset'); $format = $message->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'format'); echo "Content-Type: {$contentType}\n"; echo "Charset: {$charset}\n"; echo "Format: {$format}\n"; // Get all headers with a specific name $receivedHeaders = $message->getAllHeadersByName(HeaderConsts::RECEIVED); echo "Received headers count: " . count($receivedHeaders) . "\n"; // Iterate all headers foreach ($message->getAllHeaders() as $header) { echo $header->getName() . ": " . $header->getValue() . "\n"; } // Remove headers $message->removeSingleHeader(HeaderConsts::RECEIVED, 0); // Remove first Received header $message->removeHeader('X-Priority'); // Remove all X-Priority headers // Output: // Content-Type: text/plain; charset=ISO-8859-1; format=flowed // Charset: ISO-8859-1 // Format: flowed // Received headers count: 2 // From: alice@example.com // Subject: Test Email // [... other headers ...] ``` ### Handling charset encoding Work with different character encodings and perform charset conversions. ```php getTextPart(0); echo "Original charset: " . $part->getCharset() . "\n"; // Get content converted to UTF-8 (default) $utf8Content = $message->getTextContent(0, MailMimeParser::DEFAULT_CHARSET); echo "UTF-8 content: {$utf8Content}\n"; // Get content in specific charset $isoContent = $message->getTextContent(0, 'ISO-8859-1'); echo "ISO-8859-1 content: {$isoContent}\n"; // Override charset if incorrectly specified $part->setCharsetOverride('UTF-8', false); $correctedContent = $part->getContent(); echo "Corrected content: {$correctedContent}\n"; // Set charset override only if none specified $part->setCharsetOverride('UTF-8', true); // Work with streams in specific charset $stream = $message->getTextStream(0, 'UTF-8'); while (!$stream->eof()) { echo $stream->read(1024); } // Output: // Original charset: ISO-8859-1 // UTF-8 content: Grüße aus Deutschland! // ISO-8859-1 content: [ISO-8859-1 encoded bytes] // Corrected content: Grüße aus Deutschland! ``` ### Error handling and validation Access parsing errors and validation issues with PSR-3 logging support. ```php pushHandler(new StreamHandler('/tmp/parser.log', Logger::DEBUG)); // Create parser with logger $parser = new MailMimeParser($logger); // Parse potentially malformed message $malformedEmail = "From: broken@example.com\r\n" . "Subject: Test\r\n" . "Content-Type: multipart/mixed; boundary=\r\n" // Missing boundary value . "\r\n" . "Body content\r\n"; $message = $parser->parse($malformedEmail, false); // Check for errors if ($message->hasErrors()) { echo "Parsing errors found:\n"; foreach ($message->getAllErrors() as $error) { echo " - " . $error->getMessage() . " (Level: " . $error->getLevel() . ")\n"; echo " Context: " . $error->getPsrLevel() . "\n"; } } // Get errors with specific severity $criticalErrors = $message->getErrorsWithLogLevel(\Psr\Log\LogLevel::ERROR); echo "\nCritical errors: " . count($criticalErrors) . "\n"; // Check specific headers for errors $fromHeader = $message->getHeader('From'); if ($fromHeader && $fromHeader->hasErrors()) { echo "From header has errors\n"; } // Access attachment with error handling try { $attachment = $message->getAttachmentPart(0); if ($attachment) { $content = $attachment->getContent(); } } catch (\Exception $e) { echo "Error reading attachment: " . $e->getMessage() . "\n"; } // Output: // Parsing errors found: // - Boundary value missing in Content-Type header (Level: error) // Context: error // // Critical errors: 1 ``` ### Custom dependency injection Configure custom services and logging using PHP-DI container. ```php pushHandler(new StreamHandler('/var/log/mail-parser.log', Logger::WARNING)); // Option 1: Pass logger to single instance $parser = new MailMimeParser($logger); // Option 2: Set global logger for all instances MailMimeParser::setGlobalLogger($logger); $parser1 = new MailMimeParser(); $parser2 = new MailMimeParser(); // Also uses global logger // Option 3: Custom PHP-DI configuration $customConfig = [ LoggerInterface::class => $logger, 'custom.setting' => 'value' ]; MailMimeParser::addGlobalPhpDiContainerDefinition($customConfig); // Option 4: Per-instance custom configuration $instanceConfig = [ 'instance.specific' => 'config' ]; $parser = new MailMimeParser(null, $instanceConfig, true); // Option 5: Use container directly $container = MailMimeParser::getGlobalContainer(); $container->set('MyCustomService', function() { return new \MyNamespace\CustomService(); }); // Reset global configuration MailMimeParser::resetGlobalPhpDiContainerDefinitions(); echo "Custom configuration applied\n"; // Output: // Custom configuration applied ``` ## Summary and Integration zbateson/mail-mime-parser excels at parsing and manipulating RFC-compliant email messages in PHP applications. Primary use cases include building email clients, processing incoming mail in web applications, extracting attachments from automated emails, archiving and analyzing email data, and implementing email forwarding or transformation services. The library handles complex multipart messages, various character encodings, and attachment formats seamlessly while maintaining strict RFC compliance and providing graceful error handling for malformed messages. Integration is straightforward through Composer (`composer require zbateson/mail-mime-parser`), with support for both quick procedural usage via `Message::from()` and full object-oriented patterns with dependency injection. The library integrates with PSR-7 streams for memory-efficient processing of large messages, PSR-3 loggers for debugging and monitoring, and supports custom service configurations through PHP-DI. Advanced features include multipart/signed message support for S/MIME and PGP verification, charset conversion capabilities, and comprehensive error tracking, making it suitable for both simple email parsing tasks and complex enterprise mail processing systems.