pulli / emate
Wrapper for MailMate's emate CLI
Fund package maintenance!
the-pulli
Installs: 236
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/pulli/emate
Requires
- php: ^8.2
- symfony/mime: ^7.0|^8.0
Requires (Dev)
- egulias/email-validator: ^4.0
- laravel/pint: ^1.0
- pestphp/pest: ^3.0|^4.0
- spatie/ray: ^1.28
README
A PHP wrapper for MailMate's emate CLI tool. Provides a fluent, object-oriented interface to compose and send emails via MailMate's command-line binary.
Features
- Fluent builder pattern via static factory method
- Support for TO, CC, BCC, and Reply-To recipients
- File attachments
- Markdown body formatting
- OpenPGP and S/MIME encryption and signing
- Send-now mode for immediate delivery
- Flexible address formats: plain strings,
"Name" <email>format, arrays, and SymfonyAddressobjects - Shell-safe command generation with proper argument escaping
Installation
composer require pulli/emate
Symlink Setup
MailMate ships the emate binary inside its application bundle. Create a symlink to make it available in your $HOME/bin:
use Pulli\Emate\Emate; // Creates symlink at $HOME/bin/emate (default) Emate::symlink(); // Or specify a custom directory Emate::symlink('/usr/local/bin');
Usage
Basic Email
use Pulli\Emate\Emate; Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Hello', 'body' => 'This is the email body.', ])->mail();
With Named Recipients
Emate::from([ 'to' => 'John Doe <john@example.com>', 'from' => 'sender@example.com', 'subject' => 'Hello John', 'body' => 'Hi there!', ])->mail();
Multiple Recipients
// As an array Emate::from([ 'to' => ['alice@example.com', 'Bob <bob@example.com>'], 'from' => 'sender@example.com', 'subject' => 'Group message', 'body' => 'Hello everyone!', ])->mail(); // As a newline-separated string Emate::from([ 'to' => "alice@example.com\nBob <bob@example.com>", 'from' => 'sender@example.com', 'subject' => 'Group message', 'body' => 'Hello everyone!', ])->mail();
CC and BCC
Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'cc' => ['manager@example.com', 'team@example.com'], 'bcc' => 'archive@example.com', 'subject' => 'Update', 'body' => 'Please see the update.', ])->mail();
Reply-To
Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'reply_to' => 'replies@example.com', 'subject' => 'Hello', 'body' => 'Please reply to the other address.', ])->mail();
Symfony Address Objects
use Symfony\Component\Mime\Address; Emate::from([ 'to' => new Address('recipient@example.com', 'Recipient'), 'from' => new Address('sender@example.com', 'Sender'), 'reply_to' => new Address('replies@example.com', 'Reply Handler'), 'subject' => 'Hello', 'body' => 'Using Address objects.', ])->mail();
File Attachments
// As an array Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Files attached', 'body' => 'See attached.', 'files' => ['/path/to/report.pdf', '/path/to/data.csv'], ])->mail(); // As a newline-separated string Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'File attached', 'body' => 'See attached.', 'files' => '/path/to/report.pdf', ])->mail();
Markdown Body
Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Formatted email', 'body' => "# Heading\n\nThis is **bold** and this is *italic*.", 'markdown' => true, ])->mail();
Encryption and Signing
use Pulli\Emate\EncryptionMode; // Encrypt with OpenPGP (default) Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Secret', 'body' => 'Encrypted content.', 'encrypt' => true, ])->mail(); // Sign with OpenPGP (default) Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Signed', 'body' => 'Verified content.', 'sign' => true, ])->mail(); // Encrypt and sign with OpenPGP (default) Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Secure', 'body' => 'Encrypted and signed.', 'encrypt' => true, 'sign' => true, ])->mail(); // Sign with S/MIME Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Signed', 'body' => 'Verified content.', 'sign' => true, 'encryption_mode' => 'smime', ])->mail(); // Encrypt and sign with S/MIME, using the enum directly Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Secure', 'body' => 'Encrypted and signed.', 'encrypt' => true, 'sign' => true, 'encryption_mode' => EncryptionMode::SMIME, ])->mail();
Signature
// Set a text signature Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Hello', 'body' => 'Email body.', 'signature' => 'Best regards, PuLLi', ])->mail(); // Reference an existing signature by UUID Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Hello', 'body' => 'Email body.', 'signature' => 'uuid:12345-abcde', ])->mail();
Custom Headers
Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Hello', 'body' => 'Email body.', 'headers' => ['X-Priority: 1', 'X-Mailer: My App'], ])->mail();
Send Immediately
Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Urgent', 'body' => 'This sends immediately without queuing in drafts.', 'send_now' => true, ])->mail();
Fluent Builder
Use Emate::compose() for a fluent builder API instead of passing an options array:
use Pulli\Emate\Emate; use Pulli\Emate\EncryptionMode; // Basic email Emate::compose() ->to('recipient@example.com') ->sender('sender@example.com') ->subject('Hello') ->body('This is the email body.') ->mail(); // With all options Emate::compose() ->to(['alice@example.com', 'Bob <bob@example.com>']) ->sender('sender@example.com') ->cc('manager@example.com') ->bcc('archive@example.com') ->replyTo('replies@example.com') ->subject('Full example') ->body('# Heading\n\nMarkdown body.') ->files(['/path/to/report.pdf']) ->markdown() ->encrypt() ->sign() ->sendNow() ->encryptionMode(EncryptionMode::SMIME) ->signature('Best regards') ->header('X-Priority', '1') ->mail();
Debugging
Use debug() instead of mail() to inspect the generated shell command without executing it:
$command = Emate::from([ 'to' => 'recipient@example.com', 'from' => 'sender@example.com', 'subject' => 'Test', 'body' => 'Hello', ])->debug(); echo $command; // echo 'Hello' | $HOME/bin/emate mailto --to 'recipient@example.com' --subject 'Test' --from 'sender@example.com' --noencrypt --nosign
Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
body |
string |
'' |
Email body text |
to |
string|array|Address |
[] |
Recipient(s) |
from |
string|Address |
'' |
Sender address |
subject |
string |
'' |
Email subject |
cc |
string|array|Address |
[] |
CC recipient(s) |
bcc |
string|array|Address |
[] |
BCC recipient(s) |
reply_to |
string|Address |
'' |
Reply-to address |
files |
string|array |
[] |
File path(s) to attach |
markdown |
bool|string |
false |
Render body as Markdown (true, 'yes', 'true') |
encrypt |
bool|string |
false |
Encrypt the message (true, 'yes', 'true') |
sign |
bool|string |
false |
Sign the message (true, 'yes', 'true') |
send_now |
bool|string |
false |
Send immediately (true, 'yes', 'true') |
encryption_mode |
string|EncryptionMode |
'openpgp' |
Encryption mode: 'openpgp' or 'smime' |
signature |
string |
'' |
Signature text or 'uuid:<uuid>' to reference an existing signature |
headers |
array |
[] |
Arbitrary headers formatted as 'Name: Value' strings |
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.