NativeMailTransport.php

PHP

Path: src/Mail/NativeMailTransport.php

<?php

namespace mini\Mail;

/**
 * Mail transport using PHP's native mail() function
 *
 * Note: mail() has a peculiar API where headers and body are separate,
 * and the To/Subject headers must NOT be in the headers parameter.
 * This transport handles that conversion.
 *
 * The -f flag is used to set the envelope sender, which requires
 * safe_mode to be off or the sender to be in safe_mode_allowed_env_vars.
 */
class NativeMailTransport implements MailTransportInterface
{
    /**
     * @param string|null $additionalParams Additional parameters for mail() (e.g., '-f sender@example.com')
     */
    public function __construct(
        private ?string $additionalParams = null
    ) {}

    public function send(EmailInterface $email, string $sender, array $recipients): void
    {
        // mail() requires To header as first parameter
        $to = implode(', ', $recipients);

        // mail() requires Subject as second parameter
        $subject = $email->getSubject() ?? '';

        // Build headers string (excluding To and Subject)
        $headers = $this->buildHeaders($email);

        // Get body content
        $body = (string) $email->getBody();

        // Build additional parameters (envelope sender)
        $params = $this->additionalParams ?? '';
        if ($params === '') {
            $params = '-f' . escapeshellarg($sender);
        }

        $result = mail($to, $subject, $body, $headers, $params);

        if ($result === false) {
            throw new MailTransportException('mail() returned false');
        }
    }

    /**
     * Build headers string for mail(), excluding To and Subject
     */
    private function buildHeaders(EmailInterface $email): string
    {
        $lines = [];

        foreach ($email->getHeaders() as $name => $values) {
            // mail() handles To and Subject separately
            $lower = strtolower($name);
            if ($lower === 'to' || $lower === 'subject') {
                continue;
            }

            foreach ($values as $value) {
                $lines[] = "{$name}: {$value}";
            }
        }

        return implode("\r\n", $lines);
    }
}