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

v2.1.0 2026-01-24 08:25 UTC

This package is auto-updated.

Last update: 2026-01-24 08:25:46 UTC


README

Latest Version on Packagist Tests Total Downloads

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 Symfony Address objects
  • 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.