Отправка писем на PHP через SMTP

Отправлять письма через PHP намного проще, чем кажется. Всего-то надо сформировать правильный EML, подключиться к SMTP и отправить письмо. Ну ещё может быть, хотя и не факт, надо где-то как-то залогировать отправку, а то потом потеряют письмо, скажут, что и не было никаких писем. И виноватый опять ты, хотя ты бел и пушист. А ещё бы хорошо сделать хоть какую-то обработку ошибок, а то вдруг эксепшин, или серевер устал, или коннект не пошёл, а тут суперважный клиент. И уж совсем замечательно хранить где-то письма на случай "я потеряль", "мне не дошло", "а вышлите мне ещё раз". А ещё мы хотим отправлять письма на сайте, который видел расцвет динозавров. А ещё... мне кажется, что список ещё может расширяться не хуже нашей вселенной.

Для решения всех этих проблем я решил написать небольшой инструмент — ApMailer.

ApMailer

ApMailer — это небольшая библиотека, единственной целью которой является отправка почты. Я постарался сделать её максимально простой и удобной. Вы можете скачать всего один файл и отправлять письма и из консоли, и из web-скриптов!

Подключение ApMailer

Скачайте из репозитория файл mailer.phar или src/lib.php (если не нужна консольная версия). После этого подключите lib.php:

// Если скачали phar
include 'phar:///path/to/mailer.phar/lib.php';

// Если скачали исходный код
include '/path/to/lib.php';

После этого будет доступна функция Mailer():

Mailer()->init(include 'config.php');

$message = Mailer()->newHtmlMessage();

$message->setSubject($messageSubject);
$message->setSenderEmail($messageFrom);
$message->addRecipient($messageTo);

$message->addContent(file_get_contents('mail-header.html'));
$message->addContent($messageText);
$message->addContent(file_get_contents('mail-footer.html'));

$message->addRelatedFile('signature.png');

if (isset($_FILES['attachment']['size']) && $_FILES['attachment']['size'] > 0) {
    $message->addAttachmentFile(
        $_FILES['attachment']['tmp_name'], 
        $_FILES['attachment']['name'], 
        $_FILES['attachment']['type']
    );
}

Mailer()->sendMessage($message);

Инициализация

Перед отправкой писем Mailer надо проинициализировать. Делается это так:

Mailer()->init($config);

Конфиг представляет из себя многомерный массив, в котором указываются адрес отправителя по умолчанию, хуки для обработки ошибок и ведения логов и транспортёры:

$config = [
    'defaultFrom' => 'mymail@example.org',
    'onError'     => function($error, $message, $transport) { echo $error; },
    'afterSend'   => function($text, $message, $layer) { echo $text; },
    'transports'  => [
        // Сохранение всех писем в папке
        ['file', 'dir'  => __DIR__ .'/mails'],
        
        // Отправка писем через Yandex, используя SSL и авторизацию
        ['smtp', 'host' => 'smtp.yandex.ru', 'ssl' => true, 'port' => '465', 'login' => '****@yandex.ru', 'password' => '******'],
    ],
];

В качестве транспортов можно использовать file и smtp. Тип транспорёта всегда должен передаваться первым элементом массива, дальше идут его настройки.

Транспортёр file сохраняет письмо в виде EML в папку. Доступны следующие настройки:

Транспортёр smtp отправляет письмо через SMTP. Доступны следующие настройки:

Хуки:

Настройка defaultFrom нужна, чтобы не указывать каждый раз адрес отправителя. Если система не подразумевает работу нескольких человек, которые могут отсылать почту, то этот адрес обычно постоянный.

Рабочий пример

В качестве примера я сделал форму отправки письма:

Форма отправки письма

Пример письма:

Пример письма

Отправка почты на старых версиях PHP

Поскольку библиотека умеет работать через консоль, то можно формировать команду, которая сможет отправлять письма. Единственное, что нужно, наличие скомпилированного бинарника PHP 5.6.

function sendMail($message) {
    $keys = array(
        'subject'    => '-s',
        'from'       => '-f',
        'text'       => '-t',
        'reciient'   => array('-r'),
        'to'         => array('-r'),
        'related'    => array('-i'),
        'attachment' => array('-a'),
        'config'     => array('-c'),
    );
    
    $args = array();
    
    // Если отправляют HTML
    if (isset($message['html'])) {
        $args[] = '--html';
        
        // Подключаем шаблон
        $textArray = array();
        
        // Header
        $textArray[] = '<html><body>';
        
        if (isset($message['html'])) {
            $textArray[] = join((array) $message['html']);
        }
        
        // Footer
        $textArray[] = '</body></html>';
        
        $message['text'] = join($textArray);
    }
    
    foreach ($keys as $key => $argument) {
        if (!isset($message[$key])) {
            continue;
        }
        
        if (is_array($argument)) {
            $argument = array_shift($argument);
            
            foreach ((array) $message[$key] as $value) {
                $args[] = $argument .' '. escapeshellarg($value);
            }
        } else {
            $args[] = $argument .' '. escapeshellarg($message[$key]);
        }
    }
    
    $command = 'php5.6 /path/to/mailer.phar '. join(' ', $args);
    exec($command);
};

А дальше уже можно отправлять письма:

sendMail(array(
    'to'      => array('someone@exmaple.org', 'someoneelse@exmaple.org'),
    'subject' => 'Привет, мир!',
    'html'    => '<h1>Hello</h1>',
));

Хорошая статья, мне понравилась. Оставлю отзыв!