Письмо от PHP сломало кодировку заголовка темы - PullRequest
49 голосов
/ 08 декабря 2010

Мой PHP-скрипт отправляет электронную почту пользователям, и когда электронная почта поступает в их почтовые ящики, в строке темы ($subject) добавляются символы, такие как a^£, в конце текста моей темы. Это очевидно и проблема кодирования. С содержимым сообщения электронной почты все в порядке, только строка темы прервана.

Я искал все, но не могу найти , как правильно закодировать мою тему .

Это мой заголовок. Обратите внимание, что я использую Content-Type с charset=utf-8 и Content-Transfer-Encoding: 8bit.

//set all necessary headers
$headers = "From: $sender_name<$from>\n";
$headers .= "Reply-To: $sender_name<$from>\n";
$headers .= "X-Sender: $sender_name<$from>\n";
$headers .= "X-Mailer: PHP4\n"; //mailer
$headers .= "X-Priority: 3\n"; //1 UrgentMessage, 3 Normal
$headers .= "MIME-Version: 1.0\n";
$headers .= "X-MSMail-Priority: High\n";
$headers .= "Importance: 3\n";
$headers .= "Date: $date\n";
$headers .= "Delivered-to: $to\n";
$headers .= "Return-Path: $sender_name<$from>\n";
$headers .= "Envelope-from: $sender_name<$from>\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\n";

Ответы [ 4 ]

78 голосов
/ 08 декабря 2010

Обновление Для более практичного и актуального ответа посмотрите Ответ Палека .


Указанная кодировка символов в Content-Type описывает только кодировку символов тела сообщения, но не заголовок.Вам необходимо использовать синтаксис кодированное слово либо с кодировкой , которую можно использовать для цитирования , либо с кодировкой Base64 :

encoded-word = "=?" charset "?" encoding "?" encoded-text "?="

Вы можете использовать imap_8bit для кодируемой для печати кодировки и base64_encode для кодировки Base64:

"Subject: =?UTF-8?B?".base64_encode($subject)."?="
"Subject: =?UTF-8?Q?".imap_8bit($subject)."?="
54 голосов
/ 25 декабря 2014

TL; DR

$preferences = ['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'];
$encoded_subject = iconv_mime_encode('Subject', $subject, $preferences);
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

или

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\r\n", strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

Проблема и решение

Заголовки Content-Type и Content-Transfer-Encoding применяются только к телу вашегосообщение.Для заголовков существует механизм указания их кодировки, указанный в RFC 2047 .

Вы должны кодировать свой Subject через iconv_mime_encode(), который существует сPHP 5:

$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);

Измените input-charset в соответствии с кодировкой вашей строки $subject.Вы должны оставить output-charset как UTF-8.До PHP 5.4 используйте array() вместо [].

Now $encoded_subject is (без завершающего перевода строки)

Subject: =?UTF-8?B?VmVyeSBsb25nIHRleHQgY29udGFpbmluZyBzcGVjaWFsIGM=?=
 =?UTF-8?B?aGFyYWN0ZXJzIGxpa2UgxJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHA=?=
 =?UTF-8?B?cm9kdWNlcyBzZXZlcmFsIGVuY29kZWQtd29yZHMsIHNwYW5uaW5nIG0=?=
 =?UTF-8?B?dWx0aXBsZSBsaW5lcw==?=

для $subject, содержащее:

Very long text containing special characters like ěščřžýáíé<>?=+* produces several encoded-words, spanning multiple lines

Как это работает?

Функция iconv_mime_encode() разбивает текст, кодирует каждый фрагмент отдельно в <encoded-word> токен и сгибает пробел между ними.Кодированное слово: =?<charset>?<encoding>?<encoded-text>?=, где:

Вы можете декодировать =?CP1250?B?QWhvaiwgc3bsdGU=?= в строку UTF-8 Ahoj, světe (Hello, world на чешском языке) через iconv("CP1250", "UTF-8", base64_decode("QWhvaiwgc3bsdGU=")) или напрямую через iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8").

Кодированиев кодированные слова сложнее, потому что спецификация требует, чтобы каждый токен кодированного слова был длиной не более 75 байтов, а каждая строка, содержащая любой токен кодированного слова, должна быть длиной не более 76 байтов (включая пробел в начале строки продолжения), Не применяйте кодировку самостоятельно.Все, что вам действительно нужно знать, это то, что iconv_mime_encode() соблюдает спецификацию.

Интересное прочтение статьи Wikipedia Юникод и электронная почта .

Альтернативы

Элементарным вариантом является использование только ограниченного набора символов.ASCII гарантированно работает.ISO Latin 1 (ISO-8859-1), как user2250504 предложил , вероятно, тоже будет работать, потому что он часто используется как запасной вариант, когда не указана кодировка.Но эти наборы символов очень малы, и вы, вероятно, не сможете закодировать все символы, которые вам нужны.Более того, в RFC ничего не говорится о том, должен ли работать Latin 1 или нет.

Вы также можете использовать mb_encode_mimeheader(), как Пол Норман ответил , но это легкоиспользуйте его неправильно.

  1. Вы должны использовать mb_internal_encoding(), чтобы установить внутренне используемую кодировку функций mbstring.Функции mb_* ожидают, что входные строки будут в этой кодировке.Осторожно: второй параметр mb_encode_mimeheader() не имеет ничего общего со строкой ввода (несмотря на то, что написано в руководстве).Это соответствует <charset> в закодированном слове (см. Как это работает? выше).Входная строка перекодируется из внутренней кодировки в эту перед передачей в кодировку B или Q.

    Установка внутренней кодировки может не потребоваться с PHP 5.6, поскольку базовая mbstring.internal_encoding опция конфигурации устарела в пользу опции default_charset, которая по умолчанию установлена ​​в UTF-8.Обратите внимание, что это просто значение по умолчанию, и может быть неуместным полагаться на значения по умолчанию в вашем коде.

  2. Вы должны включить имя заголовка и двоеточие во входную строку.RFC накладывает строгое ограничение на длину строки, и оно должно сохраняться и для первой строки!Альтернативой является использование пятого параметра ($indent; последний по состоянию на сентябрь 2015 г.), но это еще менее удобно.

  3. В реализации могут быть ошибки.Даже при правильном использовании вы можете получить некорректный вывод.По крайней мере, так говорят многие комментарии на странице руководства.Мне не удалось найти никаких проблем, но я знаю, что реализация закодированных слов сложно. Если вы обнаружите потенциальные или фактические ошибки в mb_encode_mimeheader() или iconv_mime_encode(), пожалуйста, дайте мне знать в комментариях.

Естьтакже, по крайней мере, один плюс к использованию mb_encode_mimeheader(): он не всегда кодирует все содержимое заголовка, что экономит место и оставляет текст понятным для человека.Кодировка требуется только для не-ASCII частей.Вывод, аналогичный приведенному выше примеру iconv_mime_encode():

Subject: Very long text containing special characters like
 =?UTF-8?B?xJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHByb2R1Y2VzIHNldmVyYWwgZW5j?=
 =?UTF-8?B?b2RlZC13b3Jkcywgc3Bhbm5pbmcgbXVsdGlwbGUgbGluZXM=?=

Пример использования mb_encode_mimeheader():

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader("Subject: $subject", 'UTF-8');
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

Это альтернатива сниппету в TL; DR сверхуэтого поста.Вместо того, чтобы просто зарезервировать место для Subject:, он фактически помещает его туда, а затем удаляет, чтобы иметь возможность использовать его с глупым интерфейсом mail().

Если вам больше нравятся функции mbstringчем iconv, вы можете использовать mb_send_mail().Он использует mail() для внутреннего использования, но автоматически кодирует тему и текст сообщения.Опять же, используйте с осторожностью .

Заголовки, отличные от темы, требуют другой обработки

Обратите внимание, что вы не должны предполагать, что кодирование всего содержимого заголовка в порядке для всех заголовковкоторые могут содержать не-ASCII символы.Например, From, To, Cc, Bcc и Reply-To могут содержать имена адресов, которые они содержат, но могут кодироваться только имена, а не адреса.Причина в том, что токен <encoded-word> может заменять только токены <text>, <ctext> и <word> и только при определенных обстоятельствах (см. §5 RFC 2047 ).

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

17 голосов
/ 09 декабря 2010

mb_encode_mimeheader () для строк UTF-8 может быть полезно здесь, например,

$subject = mb_encode_mimeheader($subjectText,"UTF-8");
0 голосов
/ 07 декабря 2013

Сохраните файл php с соответствующей кодировкой.

В моем случае в Sublime Text я использовал следующую опцию:

Файл> Сохранить с кодировкой> Western (ISO-88591) [для бразильского португальского языка]

При этом вам не нужно использовать никакие команды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...