← Назад к заметкам

Заметки

Тему письма в mail() нужно кодировать отдельно

Короткая заметка о том, почему русская тема письма через PHP mail() может отображаться некорректно и как закодировать её в UTF-8.

Если отправлять письмо через mail(), одной установки charset=UTF-8 для тела письма недостаточно. Тему письма нужно кодировать как почтовый заголовок.

Суть проблемы

Русский текст в теме письма может прийти кракозябрами, если передать его в mail() как обычную строку без MIME-кодирования.

Коротко

Тело письма и тема письма кодируются по-разному.

Для тела письма обычно указывают заголовок с кодировкой:

Content-Type: text/plain; charset=UTF-8

Но тема письма находится в заголовке Subject. Для не-ASCII текста в заголовках нужно использовать специальный формат:

=?utf-8?b?BASE64_TEXT?=

Поэтому русскую тему перед отправкой лучше явно преобразовать через base64_encode.

Где встречается

Чаще всего проблема появляется в простых PHP-скриптах:

  • при отправке уведомлений из обработчиков;
  • при отправке писем из административных скриптов;
  • при быстрых тестовых отправках через mail();
  • при отправке писем без почтовой библиотеки;
  • при переносе старого кода на сервер с другой почтовой настройкой.

В Bitrix24 лучше по возможности использовать штатные почтовые механизмы. Но если в доработке остался прямой вызов mail(), тему нужно обработать отдельно.

Решение

Перед передачей темы в mail() нужно закодировать её в формате MIME encoded-word.

Кодирование темы

Минимальный вариант:

<?php

$subject = 'Текст письма';
$encoded_subject = '=?utf-8?b?' . base64_encode($subject) . '?=';

Лучше вынести это в отдельную функцию, чтобы не собирать строку вручную в разных местах.

<?php

/**
 * Кодирует тему письма для заголовка Subject.
 */
function encodeMailSubject(string $subject): string
{
    return '=?utf-8?b?' . base64_encode($subject) . '?=';
}

Пример отправки

Пример ниже показывает только базовую отправку через mail(). Для рабочих рассылок, очередей и повторных попыток лучше использовать отдельный почтовый механизм.

<?php

const MAIL_TO = 'user@example.com';
const MAIL_FROM = 'noreply@example.com';

$subject = 'Заявка обновлена';
$message = 'В CRM изменилась заявка.';

sendPlainTextMail(MAIL_TO, MAIL_FROM, $subject, $message);

/**
 * Отправляет простое текстовое письмо.
 */
function sendPlainTextMail(string $recipient_email, string $sender_email, string $subject, string $message): bool
{
    $encoded_subject = encodeMailSubject($subject);
    $headers = buildMailHeaders($sender_email);

    return mail($recipient_email, $encoded_subject, $message, $headers);
}

/**
 * Кодирует тему письма для заголовка Subject.
 */
function encodeMailSubject(string $subject): string
{
    return '=?utf-8?b?' . base64_encode($subject) . '?=';
}

/**
 * Собирает заголовки письма.
 */
function buildMailHeaders(string $sender_email): string
{
    $headers = [
        'MIME-Version: 1.0',
        'Content-Type: text/plain; charset=UTF-8',
        'From: ' . $sender_email,
    ];

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

Здесь кодируется только тема. Тело письма остаётся обычным UTF-8 текстом, а его кодировка указывается через заголовок Content-Type.

Источники