Mailer.php
PHP
Path: src/Mail/Mailer.php
<?php
namespace mini\Mail;
/**
* Mail sending with envelope handling and Bcc stripping
*
* This class wraps a MailTransportInterface and provides:
* - Automatic envelope sender resolution (explicit > default > From header)
* - Automatic recipient collection from To + Cc + Bcc headers
* - Bcc header stripping before sending (transport never sees Bcc)
*
* Usage:
* $mailer = new Mailer($transport);
* $mailer->send($email);
*
* // Or with explicit envelope:
* $mailer->send($email, 'bounce@example.com', ['recipient@example.com']);
*/
final class Mailer implements MailTransportInterface
{
public function __construct(
private MailTransportInterface $transport,
private ?string $defaultSender = null
) {}
/**
* Send an email
*
* @param EmailInterface $email The email to send
* @param string $sender Envelope sender - if empty, uses defaultSender or From header
* @param array<string> $recipients Envelope recipients - if empty, collects from To+Cc+Bcc
* @throws \InvalidArgumentException if no sender can be determined
* @throws MailTransportException on transport failure
*/
public function send(EmailInterface $email, string $sender = '', array $recipients = []): void
{
// Resolve envelope sender
if ($sender === '') {
$sender = $this->resolveSender($email);
}
// Resolve envelope recipients
if (empty($recipients)) {
$recipients = $this->collectRecipients($email);
}
if (empty($recipients)) {
throw new \InvalidArgumentException('No recipients specified');
}
// Strip Bcc header before sending
$email = $email->withoutHeader('Bcc');
// Delegate to transport
$this->transport->send($email, $sender, $recipients);
}
/**
* Resolve envelope sender address
*/
private function resolveSender(EmailInterface $email): string
{
// Try default sender from config
if ($this->defaultSender !== null && $this->defaultSender !== '') {
return $this->extractAddress($this->defaultSender);
}
// Try From header
$from = $email->getFrom();
if (!empty($from)) {
return $from[0]->getAddrSpec();
}
throw new \InvalidArgumentException(
'No sender specified and no From header in email'
);
}
/**
* Collect all recipient addresses from To, Cc, and Bcc headers
*
* @return array<string>
*/
private function collectRecipients(EmailInterface $email): array
{
$recipients = [];
foreach ($email->getTo() as $mailbox) {
$recipients[] = $mailbox->getAddrSpec();
}
foreach ($email->getCc() as $mailbox) {
$recipients[] = $mailbox->getAddrSpec();
}
foreach ($email->getBcc() as $mailbox) {
$recipients[] = $mailbox->getAddrSpec();
}
return array_unique($recipients);
}
/**
* Extract bare email address from a mailbox string
*/
private function extractAddress(string $mailbox): string
{
return Mailbox::fromString($mailbox)->getAddrSpec();
}
}