Заметки
Тему письма в 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.