Отправка 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.
  5. PHP_ORIG_SCRIPT

При создании нашего 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 без использования массива. В конце каждой строки нужно добавлять rn.

$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_ORIG_SCRIPT

Для удаления заголовка X-PHP-Originating-Script существует 2 способа
(если знаете ещё, поделитесь в комментариях):

  1. редактирование php.ini
  2. редактирование .htaccess

php.ini

mail.add_x_header Off

.htaccess

php_flag mail.add_x_header Off

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