Отправка mail() в php

Рассмотрим, как отправить почтовое сообщение с помощью php функции mail(). В данной статье мы создадим максимально "белую" отправку, чтобы ваше письмо не попало в папку «Спам».

Содержание:

  1. создание файла mail.php и основных переменных;
  2. формирование текстовой версии письма (text/plain) и таблицы для HTML-версии;
  3. доработка HTML-версии почтового сообщения;
  4. создание заголовков;
  5. формирование итогового сообщения письма;
  6. отправка mail();
  7. итоговый код.

Какие мы решим возможные проблемы:

  1. HTML-only message, but there is no HTML tag.
    Ваше сообщение должно содержать тег <html>.
  2. Message only has text/html MIME parts.
    Вам нужно добавить текстовую версию письма (text/plain).
  3. HTML and text parts are different.
    Убедитесь, что ТЕКСТОВАЯ версия почтового сообщения похожа на HTML версию.
  4. При отправке на почту gmail:
    Вы не можете использовать Gmail адрес, если Вы не отправляете от Gmail.

При создании нашего php кода для отправки функции mail() мы учтём моменты выше, чтобы минимизировать процент попадания письма в папку «Спам».

Создание файла mail.php и основных переменных

Создадим в корне сайта файл mail.php и добавим в него следующий код.

<?php

// Получатель(ли)

$admin_email = 'your@mail.com';

// Тема письма

$form_subject = 'Заявка с сайта site-name.ru';

// От кого

$project_name = 'email@mailer.com';

// HTML письма

$html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="ru">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>' . $form_subject . '</title>
</head>
<body>';

Мы создали 4 переменных:

  • $admin_email - список получателей (через запятую);
  • $form_subject - тема письма;
  • $project_name - отправитель письма;
  • $html - начало HTML письма (далее мы его будет дополнять контентом).
Отправка mail php

Формирование текстовой версии письма (text/plain) и таблицы для HTML-версии

Сформируем таблицу $table для HTML-версии (text/html) почтового сообщения.

Затем в переменную $plain_text добавим текстовую версию. Всё это сделаем в цикле foreach. Также предусмотрим возможный вариант $_POST['value'] в виде массива (значений чекбоксов).

$c = true;
$plain_text = '';
$table = '';

foreach ($_POST as $key => $value) {
    if (is_array($value)) $value = implode(", ", $value);

    if ($value != "" && $key != "project_name" && $key != "admin_email" && $key != "form_subject") {

        // text/html

        $table .= (($c = !$c) ? '<tr>' : '<tr style="background-color: #f8f8f8;">') . '
            <td style="padding: 10px; border: #e9e9e9 1px solid;"><b>' . $key . '</b></td>
            <td style="padding: 10px; border: #e9e9e9 1px solid;">' . $value . '</td>
        </tr>';

        // text/plain

        $plain_text .= $key . ": " . $value . "\r\n";
    }
}

Таким образом, мы обработали полученные с фронтенда данные формы.

Доработка HTML-версии почтового сообщения

В первом пункте данной статьи мы создали файл mail.php и объявили основные переменные для нашего файла, в числе которых была переменная $html. Во втором пункте мы создали переменную $table, в которой сформировали HTML-таблицу из name и value. Теперь нам необходимо добавить таблицу с данными к переменной $html.

$html .= '<table width="100%">
        <tr style="text-align: center;">
            <td style="padding: 0 10px; width: 100%; border: #e9e9e9 1px solid;" colspan="2">
                <h2>' . $form_subject . '</h2>
            </td>
        </tr>
        ' . $table . '
    </table>
</body>
</html>';

function adopt($text)
{
    return '=?UTF-8?B?' . Base64_encode($text) . '?=';
}

$boundary = "--" . md5(uniqid(time())); // генерируем разделитель

Так же мы добавили функцию adopt для кодирования темы письма и сгенерировали разделитель, т.к. почтовое сообщение будет состоять из нескольких частей Content-Type: multipart/alternative.

Создание заголовков почтового письма

Сформируем заголовки в виде массива (для PHP >= 7.2).

$headers = array(
    'MIME-Version' => '1.0',
    'Date' => date('r (T)'),
    'From' => 'Имя отправителя (компании) <' . $project_name . '>',
    'Reply-To' => $project_name,
    'X-Mailer' => 'PHP/' . phpversion(),
    'Content-Type' => 'multipart/alternative; boundary="' . $boundary . '"',
);

Для более старых версий php необходимо создавать $headers без использования массива. В конце каждой строки нужно добавлять \r\n

$headers .= 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Date: ' . date('r (T)') . "\r\n";
$headers .= 'From: Имя отправителя (компании) <' . $project_name . '>' . "\r\n";
$headers .= 'Reply-To: ' . $project_name . "\r\n";
$headers .= 'X-Mailer: PHP/' . phpversion() . "\r\n";
$headers .= 'multipart/alternative; boundary="' . $boundary . '"';

Формирование итогового сообщения письма

Собираем воедино сообщение, которое будем использовать для отправки 3 параметром функции mail().

// Текстовая версия письма 

$message_plain_text .= "--$boundary" . "\n";

$message_plain_text .= 'Content-Type: text/plain; charset=utf-8' . "\n";
$message_plain_text .= 'Content-Transfer-Encoding: 8bit' . "\n\n";
$message_plain_text .= $form_subject . "\r\n" . $plain_text . "\n";

// HTML-версия письма

$message_html .= "--$boundary" . "\n";

$message_html .= 'Content-Type: text/html; charset=utf-8' . "\n";
$message_html .= 'Content-Transfer-Encoding: 8bit' . "\n\n";
$message_html .= $html . "\n";

$multipart_alternative = $message_plain_text . $message_html . "--$boundary--" . "\n";

На что стоит обратить внимание:

Желательно чтобы заголовки From, Reply-To и Return-Path совпадали.

Только вместо Return-Path мы укажем обратный адрес e-mail в 5 параметре функции mail(). Это позволит избежать проблему №4 в начале данной статьи.

Отправка mail() в php с проверкой

Все необходимые переменные для заполнения параметров мы подготовили, осталось отправить письмо и в случае ошибки вернуть сообщение об этом.

if (!mail($admin_email, adopt($form_subject), $multipart_alternative, $headers, "-f " . $project_name)) {
    $error = error_get_last()['message'];
    print_r($error);
}

Итоговый код

В результате наш mail.php должен выглядеть примерно так:

<?php

// Получатель(ли)

$admin_email = 'your@mail.com';

// Тема письма

$form_subject = 'Заявка с сайта site-name.ru';

// От кого

$project_name = 'email@mailer.com';

// HTML письма

$html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="ru">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>' . $form_subject . '</title>
</head>
<body>';

$c = true;
$plain_text = '';
$table = '';

foreach ($_POST as $key => $value) {
    if (is_array($value)) $value = implode(", ", $value);

    if ($value != "" && $key != "project_name" && $key != "admin_email" && $key != "form_subject") {

        // text/html

        $table .= (($c = !$c) ? '<tr>' : '<tr style="background-color: #f8f8f8;">') . '
            <td style="padding: 10px; border: #e9e9e9 1px solid;"><b>' . $key . '</b></td>
            <td style="padding: 10px; border: #e9e9e9 1px solid;">' . $value . '</td>
        </tr>';

        // text/plain

        $plain_text .= $key . ": " . $value . "\r\n";
    }
}

$html .= '<table width="100%">
        <tr style="text-align: center;">
            <td style="padding: 0 10px; width: 100%; border: #e9e9e9 1px solid;" colspan="2">
                <h2>' . $form_subject . '</h2>
            </td>
        </tr>
        ' . $table . '
    </table>
</body>
</html>';

function adopt($text)
{
    return '=?UTF-8?B?' . Base64_encode($text) . '?=';
}

$boundary = "--" . md5(uniqid(time())); // генерируем разделитель


$headers = array(
    'MIME-Version' => '1.0',
    'Date' => date('r (T)'),
    'From' => 'Имя отправителя (компании) <' . $project_name . '>',
    'Reply-To' => $project_name,
    'X-Mailer' => 'PHP/' . phpversion(),
    'Content-Type' => 'multipart/alternative; boundary="' . $boundary . '"',
);

// Текстовая версия письма

$message_plain_text .= "--$boundary" . "\n";

$message_plain_text .= 'Content-Type: text/plain; charset=utf-8' . "\n";
$message_plain_text .= 'Content-Transfer-Encoding: 8bit' . "\n\n";
$message_plain_text .= $form_subject . "\r\n" . $plain_text . "\n";

// HTML-версия письма

$message_html .= "--$boundary" . "\n";

$message_html .= 'Content-Type: text/html; charset=utf-8' . "\n";
$message_html .= 'Content-Transfer-Encoding: 8bit' . "\n\n";
$message_html .= $html . "\n";

$multipart_alternative = $message_plain_text . $message_html . "--$boundary--" . "\n";

if (!mail($admin_email, adopt($form_subject), $multipart_alternative, $headers, "-f " . $project_name)) {
    $error = error_get_last()['message'];
    print_r($error);
}

Чтобы не попасть в папку спам в php обработчике мы сделали всё от нас зависящее. Остальные настройки вам необходимо делать на сервере: SPF, DKIM, DMARC, PTR.

admin