cosmira / outlook-msg
Read and write Microsoft Outlook MSG files in modern PHP
Requires
- php: ^8.2
- brick/math: ^0.12|^0.13|^0.14|^0.15|^0.16|^0.17|^0.18|^0.19
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- driftingly/rector-laravel: ^2.1
- infection/infection: ^0.32|^0.33
- laravel/pint: ^1.2
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5|^12.0|^13.0
- rector/rector: ^2.3
- symfony/process: ^7.0
- symfony/var-dumper: ^7.0|^8.0
This package is not auto-updated.
Last update: 2026-08-19 11:39:51 UTC
README
Read and write Microsoft Outlook .msg files with a clean, fluent PHP API.
It gives you a clean API for subjects, bodies, recipients, attachments, embedded messages, and raw MAPI properties, while still leaving the low-level pieces available when you need them.
Installation
composer require cosmira/outlook-msg
Why You'll Like It
- Read Outlook
.msgfiles into a friendlyMessageobject - Work with recipients and attachments through fluent collections
- Access subject, sender, headers, plain text, HTML, and RTF through expressive methods
- Create new
.msgfiles with a clean builder API - Attach regular files, inline files, and embedded
.msgmessages - Preserve unmapped MAPI properties for round-trip scenarios
- Drop down to low-level compound file and RTF helpers when needed
Quick Start
Read a message
use Cosmira\OutlookMessage\Attachment; use Cosmira\OutlookMessage\Message; $message = Message::fromPath('example.msg'); echo $message->subject(); echo $message->senderName(); echo $message->preferredBody(); $message ->attachments() ->filter(static fn (Attachment $attachment): bool => $attachment->isInline()) ->each(static fn (Attachment $attachment): void => print $attachment->contentId());
Create a message
use Cosmira\OutlookMessage\Message; Message::make() ->from('Jane Doe', 'jane@example.com') ->to('Abigail', 'abigail@example.com') ->subject('Ship it') ->text('The plain text body') ->save('message.msg');
Reading Messages
use Cosmira\OutlookMessage\Message; $message = Message::fromPath('example.msg'); echo $message->subject(); echo $message->senderName(); echo $message->senderEmail(); echo $message->preferredBody();
Use Message::from($binary) for an in-memory MSG payload and Message::fromPath($path) for a file.
Working With Recipients
use Cosmira\OutlookMessage\Recipient; $message ->to() ->each(function (Recipient $recipient) { printf("%s <%s>\n", $recipient->name() ?? '', $recipient->email() ?? ''); }); // Additional recipient groups: $message->cc() ->each(static fn (Recipient $recipient): void => print $recipient->email()); // Formatted header lines from the original message: echo $message->displayTo(); echo $message->displayCc(); echo $message->displayBcc();
Working With Attachments
use Cosmira\OutlookMessage\Attachment; $message ->attachments() ->each(static function (Attachment $attachment, int $index): void { $name = $attachment->name() ?? "attachment_{$index}"; file_put_contents(__DIR__."/out/{$name}", $attachment->data()); });
Inline attachments
use Cosmira\OutlookMessage\Attachment; $message ->attachments() ->filter(fn (Attachment $attachment) => $attachment->isInline()) ->each(fn (Attachment $attachment) => print $attachment->contentId());
Embedded .msg attachments
use Cosmira\OutlookMessage\Attachment; $message ->attachments() ->filter(fn (Attachment $attachment) => $attachment->isEmbedded()) ->each(fn (Attachment $attachment) => print $attachment->message()?->subject());
Replacing attachment payloads
Parsed messages are edited directly. data() presents both regular files and embedded messages as bytes, so recursive
processors do not need separate branches:
foreach ($message->attachments() as $attachment) { $attachment->withData( $processor($attachment->data()) ); } $message->save('processed.msg');
Reference, storage, and web-reference attachment methods throw a typed
UnsupportedAttachmentMethodException when their payload is read or replaced.
Bodies: HTML, Plain Text, and RTF
If you just want the best available body, use:
$body = $message->preferredBody();
If you want explicit access:
$message->body()for plain text$message->bodyHtml()for HTML$message->bodyRtf()for decompressed RTF text
To work with raw compressed RTF payloads directly:
use Cosmira\OutlookMessage\Rtf\RtfDecompressor; $rtf = RtfDecompressor::decompress($rawRtfBinary);
Creating Messages
The smoothest writing experience is the fluent builder API:
use DateTimeImmutable; use DateTimeZone; use Cosmira\OutlookMessage\Attachment; use Cosmira\OutlookMessage\Message; $draft = Message::make() ->from('Jane Doe', 'jane@example.com') ->subject('Ship it') ->text('The plain text body') ->html('<p>The <strong>HTML</strong> body</p>') ->withHeaders("X-App: outlook-msg\r\n") ->sentAt(new DateTimeImmutable('2024-01-01 10:00:00', new DateTimeZone('UTC'))) ->to('Abigail', 'abigail@example.com') ->cc('Jess', 'jess@example.com') ->bcc('Ops', 'ops@example.com') ->attach(Attachment::fromData('Remember the meeting at 11:40', 'notes.txt')) ->attach(Attachment::fromData($logoBinary, 'logo.png')->inline('cid:logo')); $draft->save('message.msg');
You can still use MessageBuilder::make() and MessageWriter::make() directly if you prefer the lower-level writer
entry points.
Attachment Objects
The same Attachment object is used when reading, editing, and creating messages:
use Cosmira\OutlookMessage\Attachment; $report = Attachment::fromData($pdfBinary, 'report.pdf') ->withMime('application/pdf'); $lazy = Attachment::fromData(fn () => loadReport(), 'report.pdf'); $fromDisk = Attachment::fromPath('/tmp/report.pdf'); $inline = Attachment::fromData($logoBinary, 'logo.png')->inline('cid:logo'); Message::make()->attach($report);
Embedded Message Attachments
You can attach one .msg inside another:
use Cosmira\OutlookMessage\Attachment; use Cosmira\OutlookMessage\Message; $nested = Message::from(Message::make() ->from('Nested Sender', 'nested@example.com') ->subject('Nested message') ->text('Hello from inside') ->toBinary()); $draft = Message::make() ->from('Parent Sender', 'parent@example.com') ->subject('Parent message') ->attach(Attachment::fromMessage($nested, 'forwarded.msg')); $embedded = $draft->attachments()[0]->message();
Raw MAPI Properties
Known message fields are mapped onto friendly methods. Everything else can still be preserved and inspected through raw properties:
$raw = $message->rawProperties();
This is useful when:
- you need round-trip fidelity
- you care about Outlook-specific metadata not mapped by the library
- you want to inspect or write custom MAPI values
Low-Level APIs
The package also includes lower-level APIs for advanced scenarios:
Cosmira\OutlookMessage\CompoundFile\CompoundFilefor CFBF/OLE storage accessCosmira\OutlookMessage\Support\BinaryBufferfor binary readsCosmira\OutlookMessage\Rtf\RtfDecompressorfor compressed RTF payloads
Testing
php vendor/bin/phpunit php vendor/bin/rector process php vendor/bin/phpstan analyse --no-progress