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);
问题与解决方案
和标头仅适用于您的邮件正文。 对于标头,有一种机制可以指定RFC 2047中指定的编码。
您应该通过对进行编码,该版本自PHP 5起就存在:
$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);
更改以匹配您的字符串的编码。您应将保留为。在PHP 5.4之前,请使用iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8")而不是[]。
现在是(不包含换行符)
Subject: =?UTF-8?B?VmVyeSBsb25nIHRleHQgY29udGFpbmluZyBzcGVjaWFsIGM=?=
=?UTF-8?B?aGFyYWN0ZXJzIGxpa2UgxJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHA=?=
=?UTF-8?B?cm9kdWNlcyBzZXZlcmFsIGVuY29kZWQtd29yZHMsIHNwYW5uaW5nIG0=?=
=?UTF-8?B?dWx0aXBsZSBsaW5lcw==?=
为包含:
Very long text containing special characters like ěščřžýáíé<>?=+* produces several encoded-words, spanning multiple lines
它是如何工作的?
函数拆分文本,将每个片段分别编码为令牌,并折叠它们之间的空格。 编码字为,其中:
是(对于Base 64 –请参见)或(对于Quoted可打印–请参见),
是用编码的字符串,该字符串在解码后具有字符集。
您可以通过或直接通过iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8")将解码为UTF-8字符串(捷克语中为)。
编码为编码字比较复杂,因为规范要求每个编码字令牌的长度最多为75个字节,并且包含任何编码字令牌的每一行的长度都必须最大为76个字节(包括连续行开头的空白) )。 不要自己实现编码。 您真正需要知道的是遵守规范。
有趣的相关阅读是Wikipedia文章Unicode和电子邮件。
备择方案
一个基本的选择是仅使用一组受限字符。 ASCII保证有效。 正如user2250504建议的那样,ISO Latin 1(ISO-8859-1)也可能也可以工作,因为在未指定编码时,它通常用作备用。 但是这些字符集很小,您可能无法对所需的所有字符进行编码。 此外,RFC并未提及Latin 1是否应工作。
您也可以使用,就像Paul Norman回答的那样,但是使用起来很不正确。
您必须使用来设置mbstring函数的内部使用的编码。 函数期望输入字符串采用这种编码。 注意:的第二个参数与输入字符串无关(尽管手册说了什么)。 它对应于编码词中的(请参见上面的工作原理)。 在将输入字符串传递给B或Q编码之前,将从内部编码重新编码为该字符串。
自PHP 5.6起,可能不需要设置内部编码,因为不推荐使用基础的配置选项,而推荐使用选项,此选项默认情况下已设置为UTF-8。 请注意,这只是一个默认值,在代码中依赖默认值可能是不合适的。
您必须在输入字符串中包含标题名称和冒号。 RFC对行长施加了严格的限制,它也必须适用于第一行! 另一种方法是摆弄第五个参数(;最后一个参数截至2015年9月),但这更加不方便。
该实现可能存在错误。 即使正确使用,也可能会损坏输出。 至少这是手册页上的许多评论所说的。 我没有找到任何问题,但是我知道编码单词的实现很棘手。 如果您发现或中潜在或实际的错误,请在评论中告知我。
使用至少还有一个好处:它并不总是对所有标头内容进行编码,这样可以节省空间并使文本易于阅读。 仅非ASCII部分需要编码。 与上面的示例类似的输出为:
Subject: Very long text containing special characters like
=?UTF-8?B?xJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHByb2R1Y2VzIHNldmVyYWwgZW5j?=
=?UTF-8?B?b2RlZC13b3Jkcywgc3Bhbm5pbmcgbXVsdGlwbGUgbGluZXM=?=
的使用示例:
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中的代码段的替代方法。 实际上,它不只是保留的空间,而是将其放到那里,然后将其删除,以便能够与的愚蠢接口一起使用。
如果您更喜欢mbstring函数而不是iconv函数,则可能要使用。它内部使用,但是会自动编码消息的主题和正文。 同样,请小心使用。
除主题以外的标题需要不同的处理
请注意,对于所有可能包含非ASCII字符的标头,您都不能假定对标头的全部内容进行编码是可以的。 例如。 “发件人”,“收件人”,“抄送”,“密件抄送”和“答复收件人”可能包含其所包含地址的名称,但只能对名称进行编码,而不能对地址进行编码。 原因是令牌仅在某些情况下才可以替换、和令牌(请参阅RFC 2047的§5)。
其他标题中非ASCII文本的编码是一个相关但不同的问题。 如果您想了解更多有关此主题的信息,请搜索。 如果找不到答案,请提出另一个问题,并在评论中指出我的问题。