zbateson/mail-mime-parser

repository·master·Indexed 19 days ago

https://github.com/zbateson/mail-mime-parser

A testable, PSR-compliant PHP library for parsing email messages in the Internet Message Format (RFC 822, RFC 2822, RFC 5322). It provides a modern alternative to PHP's built-in imap functions and Pear libraries, supporting the extraction of headers, subjects, body content, and attachments. The library supports dependency injection via PHP-DI and can be extended with companion packages for S/MIME and PGP/MIME encryption and signing.

Tokens
3.9K
Snippets
10
Records
13
Agent score
69%

What's inside zbateson/mail-mime-parser

  1. Add S/MIME or PGP/MIME support

    master

    By default, the parser does not handle encryption or signing. You can add this functionality by installing companion packages. Once installed, encrypted or signed messages will be automatically detected and decrypted during the parse() process.

    • S/MIME: Install zbateson/mmp-crypt-smime (requires PHP's OpenSSL extension).
    • PGP/MIME: Install zbateson/mmp-crypt-gpg (requires PEAR's Crypt_GPG).
  2. Configure dependency injection and logging in MailMimeParser

    master

    The MailMimeParser uses PHP-DI for dependency injection, allowing you to override or provide specialized classes (like encryption or signing plugins). You can configure this at two levels:

    Instance Level

    When creating a new instance via the constructor, you can provide a specific LoggerInterface or a PHP-DI configuration (array, string, or DefinitionSource). This configuration only affects that specific instance.

    $parser = new MailMimeParser(
        $myLogger,           // LoggerInterface
        $myDiConfig,         // array|string|DefinitionSource
        true                 // $useGlobalDefinitions (default: true)
    );

    Global Level

    To affect all instances of MailMimeParser created in your application, use the static methods:

    • addGlobalPhpDiContainerDefinition(...): Adds a new definition to the global container.
    • setGlobalPhpDiConfigurations(array $phpDiConfigs, bool $useDefaultDefinitionsFile = true): Replaces all global definitions with the provided ones.
    • setGlobalLogger(LoggerInterface $logger): Sets a logger to be used by all instances.
    • setFallbackCharset(string $charset): Sets the global fallback charset for text parts that do not declare one.
    // Instance-specific configuration
    $parser = new MailMimeParser($logger, $diConfig);
    
    // Global configuration
    MailMimeParser::addGlobalPhpDiContainerDefinition($diConfig);
    MailMimeParser::setGlobalLogger($logger);
  3. Handle attachments

    master

    Attachments can be retrieved by index using getAttachmentPart(int $index). The returned object allows you to inspect metadata and extract the content.

    • getHeaderValue(string $headerName): Get metadata like Content-Type.
    • getHeaderParameter(string $headerName, string $parameterName): Get specific parameters from a header (e.g., charset from Content-Type).
    • getContent(): Returns the attachment contents.
    • getContentStream(): Returns a decoded stream of the attachment.
    • saveContent(string $path): Writes the attachment to a file.
    • saveContent($stream): Copies the attachment to a provided stream.
    use ZBateson\MailMimeParser\Header\HeaderConsts;
    
    $att = $message->getAttachmentPart(0);                 // Get first attachment
    echo $att->getHeaderValue(HeaderConsts::CONTENT_TYPE); // e.g. "text/plain"
    echo $att->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'charset');
    
    // Saving the attachment
    $att->saveContent('my-file.ext');                     // Save to file
    
    // Using streams
    $stream = $att->getContentStream();                    // Decoded stream
    $dest = \GuzzleHttp\Psr7\stream_for(fopen('my-file.ext', 'w'));
    \GuzzleHttp\Psr7\copy_to_stream($stream, $dest);
  4. Read message headers and content

    master

    Once a message is parsed into a Message object, you can access headers, subjects, and body content. Use HeaderConsts for standard header names.

    • getHeaderValue(string $headerName): Returns the raw string value of a header.
    • getHeader(string $headerName): Returns a structured header object (e.g., AddressHeader).
    • getSubject(): Returns the email's subject.
    • getTextContent(): Returns the plain text body.
    • getHtmlContent(): Returns the HTML body.
    use ZBateson\MailMimeParser\Header\HeaderConsts;
    
    echo $message->getHeaderValue(HeaderConsts::FROM);     // user@example.com
    echo $message->getSubject();                           // The email's subject
    echo $message->getTextContent();                       // Plain text body
    echo $message->getHeader('X-Foo');                     // Custom headers
    
    // Accessing structured address data
    echo $message->getHeader(HeaderConsts::FROM)
        ->getPersonName();                                 // Person Name
    
    echo $message->getHeader(HeaderConsts::TO)
        ->getAddresses()[0]
        ->getPersonName();                                 // Person Name from first address
  5. Parse MIME messages

    master

    You can parse email messages using either the MailMimeParser class or the static Message::from() method. Both approaches accept a string, a resource, or a Psr7\\StreamInterface.

    When passing a resource (like a file handle), you can pass true as the second argument to parse() to instruct the parser to attach the handle and close it automatically when the returned Message object is destroyed.

    use ZBateson\MailMimeParser\MailMimeParser;
    use ZBateson\MailMimeParser\Message;
    
    // Option 1: Using MailMimeParser instance
    $mailParser = new MailMimeParser();
    $handle = fopen('file.mime', 'r');
    $message = $mailParser->parse($handle, false);
    
    // Option 2: Using procedural static method
    $string = "Content-Type: text/plain\r\nSubject: Test\r\n\r\nMessage";
    $message = Message::from($string, false);
  6. Handle and inspect errors using the Error class

    master

    The ZBateson\MailMimeParser\Error class represents an error or notice that occurred on a specific object within the parser. It encapsulates a message, a PSR-3 compliant log level, the associated ErrorBag object, and an optional Throwable exception.

    When processing mail, you can use this class to inspect the severity of issues encountered. The isPsrLevelGreaterOrEqualTo(string $minLevel) method allows you to filter errors based on their severity relative to a minimum PSR log level (e.g., checking if an error is at least as severe as LogLevel::ERROR).

    use ZBateson\MailMimeParser\Error;
    use Psr\Log\LogLevel;
    
    // Assuming $error is an instance of Error retrieved from an ErrorBag
    if ($error->isPsrLevelGreaterOrEqualTo(LogLevel::ERROR)) {
        echo "A critical error occurred: " . $error->getMessage();
        if ($error->getException()) {
            echo " Exception: " . $error->getException()->getMessage();
        }
    }
  7. Set the global fallback charset

    master

    By default, MailMimeParser follows RFC 2045 and uses ISO-8859-1 for text content parts that do not declare a charset. Since many modern messages omit the charset but are actually encoded in UTF-8, you can globally override this behavior using setFallbackCharset().

    MailMimeParser::setFallbackCharset('UTF-8');
  8. Inspect Error properties

    master

    The Error class provides several methods to retrieve details about a specific error event:

    • getMessage(): string: Returns the error message.
    • getPsrLevel(): string: Returns the PSR-3 string log level (e.g., LogLevel::WARNING).
    • getClass(): string: Returns the class name of the object where the error occurred.
    • getObject(): ErrorBag: Returns the ErrorBag instance associated with the error.
    • getException(): ?Throwable: Returns the underlying Throwable exception if one was captured, otherwise null.
  9. Manage message part children with PartChildrenContainer

    master

    The PartChildrenContainer class is a specialized container used to hold IMessagePart items, typically within an IMultiPart object. It implements both ArrayAccess and RecursiveIterator, allowing you to treat the container like an array while also enabling recursive traversal of nested MIME structures.

    Key Capabilities

    • Array-like Access: Use standard array syntax to access, set, or unset parts.
    • Recursive Iteration: Use it in foreach loops or with RecursiveIterator methods to traverse the entire MIME tree. The hasChildren() and getChildren() methods allow you to check if the current part is an IMultiPart and retrieve its children.
    • Adding/Removing Parts: Use add() to insert a part at a specific index or at the end, and remove() to delete a specific part instance.

    Methods

    MethodDescription
    add(IMessagePart $part, ?int $position = null)Adds a part. If $position is null or out of bounds, it appends to the end.
    remove(IMessagePart $part)Removes the specified part and returns its integer position, or null if not found.
    hasChildren(): boolReturns true if the current element in the iteration is an IMultiPart.
    getChildren(): ?RecursiveIteratorReturns the child iterator of the current IMultiPart, or null if the current element is not a multipart.
    // Example of adding and accessing parts
    $container = new \ZBateson\MailMimeParser\Message\PartChildrenContainer();
    $container->add($somePart);
    $container->add($anotherPart, 0); // Insert at the beginning
    
    $firstPart = $container[0];
  10. Manage MIME part headers with PartHeaderContainer

    master

    The PartHeaderContainer class is used to maintain and manipulate a collection of headers for a specific MIME part. It provides methods to add, retrieve, update, and remove headers. It handles case-insensitivity by normalizing header names and supports retrieving headers as specific IHeader implementations.

    Key capabilities:

    • Adding/Setting: Use add($name, $value) to append a header or set($name, $value, $offset) to update an existing header at a specific position or create it if it doesn't exist.
    • Retrieval: Use get($name, $offset) to get an IHeader object, or getAs($name, $iHeaderClass, $offset) to retrieve a header cast to a specific class type.
    • Bulk Operations: Use getAll($name) to retrieve all headers matching a specific name as an array of IHeader objects.
    • Removal: Use remove($name, $offset) to delete a specific instance or removeAll($name) to delete all headers with that name.
    • Iteration: The container implements IteratorAggregate, allowing you to loop over headers directly as ['Name', 'Value'] pairs.
    // Example of adding and retrieving headers
    $container = new PartHeaderContainer($logger, $headerFactory);
    $container->add('Content-Type', 'text/plain');
    $container->add('X-Custom-Header', 'Value1');
    $container->add('X-Custom-Header', 'Value2');
    
    // Get the first Content-Type header
    $contentType = $container->get('Content-Type');
    
    // Get all X-Custom-Header instances
    $customHeaders = $container->getAll('X-Custom-Header');
    
    // Iterate through all headers
    foreach ($container as [$name, $value]) {
        echo "$name: $value\n";
    }
  11. Parse a MIME message with MailMimeParser

    master

    To parse a MIME message, instantiate MailMimeParser and call the parse() method. You can pass a resource handle, a Psr\Http\Message\StreamInterface object, or a raw string containing the MIME message.

    If you provide a resource handle or a stream, you should decide how the lifecycle of that resource is managed using the $autoClose parameter:

    • Set $autoClose to true to have the resource automatically closed when the returned IMessage object is destroyed.
    • Set $autoClose to false if you want to manage the resource lifecycle manually.

    Note: If the provided stream is not seekable, the parser will automatically wrap it in a CachingStream to ensure functionality.

    $parser = new MailMimeParser();
    
    // Example 1: Parsing from a file resource with automatic closing
    $message = $parser->parse(fopen('path/to/file.txt'), true);
    
    // Example 2: Parsing from a string
    $message = $parser->parse("MIME-Version: 1.0\nSubject: Hello\n\nBody content", false);