Skip to content

marko/mail

Mail contracts and message building --- compose emails with a fluent API and send them through any mail driver. This package provides the MailerInterface, the fluent Message builder, Address and Attachment value objects, and an optional ViewMailer decorator for template rendering. It contains no transport implementation; install a driver like marko/mail-smtp or marko/mail-log for actual delivery. Includes a mail:test CLI command for verifying configuration.

This package defines contracts only. Install a driver for implementation:

  • marko/mail-smtp --- SMTP transport
  • marko/mail-log --- Log-based transport (development/testing)
Terminal window
composer require marko/mail

Note: You also need an implementation package such as marko/mail-smtp or marko/mail-log.

Inject the mailer and build messages with the fluent API:

use Marko\Mail\Contracts\MailerInterface;
use Marko\Mail\Message;
class WelcomeMailer
{
public function __construct(
private MailerInterface $mailer,
) {}
public function sendWelcome(
string $email,
string $name,
): void {
$message = Message::create()
->to($email, $name)
->from('hello@example.com', 'My App')
->subject('Welcome!')
->html('<h1>Welcome, ' . $name . '!</h1>')
->text('Welcome, ' . $name . '!');
$this->mailer->send($message);
}
}

Attach files from disk or embed inline images:

use Marko\Mail\Message;
$message = Message::create()
->to('user@example.com')
->from('noreply@example.com')
->subject('Your Invoice')
->html('<p>Please find your invoice attached.</p>')
->attach('/path/to/invoice.pdf')
->embed('/path/to/logo.png', 'logo-id');

When marko/view is installed, use the ViewMailer to render templates:

use Marko\Mail\Message;
$message = Message::create()
->to('user@example.com')
->from('noreply@example.com')
->subject('Order Confirmation')
->view('emails/order-confirmation')
->with(['order' => $order]);

The MailConfig class provides typed access to mail configuration values:

use Marko\Mail\Config\MailConfig;
class MyService
{
public function __construct(
private MailConfig $mailConfig,
) {}
public function setup(): void
{
$driver = $this->mailConfig->driver();
$fromAddress = $this->mailConfig->fromAddress();
$fromName = $this->mailConfig->fromName();
$driverConfig = $this->mailConfig->driverConfig('smtp');
}
}
CommandDescription
marko mail:test <email>Send a test email to verify mail configuration
marko mail:test <email> --subject="Custom Subject"Send a test email with a custom subject
use Marko\Mail\Contracts\MailerInterface;
use Marko\Mail\Message;
public function send(Message $message): bool;
public function sendRaw(string $to, string $raw): bool;

Both methods throw TransportException on delivery failure.

use Marko\Mail\Message;
public static function create(): self;
public function to(string $email, ?string $name = null): self;
public function cc(string $email, ?string $name = null): self;
public function bcc(string $email, ?string $name = null): self;
public function from(string $email, ?string $name = null): self;
public function replyTo(string $email, ?string $name = null): self;
public function subject(string $subject): self;
public function html(string $html): self;
public function text(string $text): self;
public function attach(string $path, ?string $name = null, ?string $mimeType = null): self;
public function embed(string $path, string $contentId, ?string $name = null, ?string $mimeType = null): self;
public function header(string $name, string $value): self;
public function priority(int $priority): self;
public function view(string $template): self;
public function with(array|string $key, mixed $value = null): self;
use Marko\Mail\Address;
readonly class Address
{
public string $email;
public ?string $name;
public function toString(): string;
}
use Marko\Mail\Attachment;
public static function fromPath(string $path, ?string $name = null, ?string $mimeType = null): self;
public static function fromContent(string $content, string $name, string $mimeType = 'application/octet-stream'): self;
public static function inline(string $path, string $contentId, ?string $name = null, ?string $mimeType = null): self;
use Marko\Mail\Config\MailConfig;
public function ensureConfigExists(): void;
public function driver(): string;
public function fromAddress(): string;
public function fromName(): string;
public function driverConfig(string $driver): array;
ExceptionDescription
MailExceptionBase exception for all mail errors --- includes getContext() and getSuggestion() methods
MessageExceptionThrown for invalid email addresses, missing attachments, or no recipients
TransportExceptionThrown on delivery failures --- connection errors, TLS failures, authentication failures, unexpected SMTP responses