我的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";
答案 0 :(得分:76)
更新要获得更实用和最新的答案,请查看Palec’s answer。
Content-Type 中指定的字符编码仅描述邮件正文的字符编码,但不描述标题。您需要将encoded-word syntax与quoted-printable encoding或Base64 encoding一起使用:
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)."?="
答案 1 :(得分:51)
$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中指定的编码。
您应该通过iconv_mime_encode()
对Subject
进行编码,该版本自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()
代替[]
。
现在$encoded_subject
是(没有尾随换行符)
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>
标记,并folds它们之间的空格。编码的单词是=?<charset>?<encoding>?<encoded-text>?=
,其中:
<encoding>
要么B
(适用于Base 64 - 请参阅base64_encode()
)或Q
(适用于Quoted-printable - 请参阅quoted_printable_encode()
),<encoded-text>
使用<encoding>
进行字符串编码,解码后会有{charset <charset>
。您可以通过=?CP1250?B?QWhvaiwgc3bsdGU=?=
或直接通过Ahoj, světe
将Hello, world
解码为UTF-8字符串iconv("CP1250", "UTF-8", base64_decode("QWhvaiwgc3bsdGU="))
(捷克语iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8")
)。
编码到编码的单词更复杂,因为规范要求每个编码字令牌长度最多为75个字节,并且每行包含任何编码字令牌的长度必须至多为76个字节(包括开头时的空白)延续线)。 不要自己实现编码。您真正需要知道的是iconv_mime_encode()
尊重规范。
有趣的相关阅读是维基百科文章Unicode and email。
一个基本选项是仅使用一组受限制的字符。 ASCII保证可以工作。 ISO Latin 8(ISO-8859-1),user2250504 suggested,也可能会起作用,因为在未指定编码时,它通常用作后备。但是这些字符集非常小,你可能无法编码你想要的所有字符。此外,RFC并未说明拉丁语1是否应该起作用。
您也可以使用mb_encode_mimeheader()
作为Paul Norman answered,但很容易错误地使用它。
您必须使用mb_internal_encoding()
来设置mbstring函数的内部编码。 mb_*
函数期望输入字符串处于此编码状态。注意:mb_encode_mimeheader()
的第二个参数与输入字符串无关(尽管手册中有说明)。它对应于编码字中的<charset>
(请参阅上面的它是如何工作的?)。在传递给B或Q编码之前,输入字符串从内部编码重新编码到此编码。
自PHP 5.6起,可能不需要设置内部编码,因为基础mbstring.internal_encoding
配置选项已被弃用,而有利于default_charset
选项,默认情况下已设置为UTF-8,因为。请注意,这只是默认设置,依赖代码中的默认值可能不合适。
您必须在输入字符串中包含标题名称和冒号。 RFC对行长度施加了强烈的限制,它也必须适用于第一行!另一种方法是摆弄第五个参数($indent
;截至2015年9月的最后一个参数),但这更不方便。
实施可能有错误。即使使用正确,您也可能会损坏输出。至少这是手册页上的许多评论所说的。我没有设法找到任何问题,但我知道编码的单词的实现是棘手的。 如果您在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()
的愚蠢接口一起使用。
如果您比mttring函数更喜欢iconv函数,则可能需要使用mb_send_mail()
。它在内部使用mail()
,但会自动编码消息的主题和正文。再次,use with care。
请注意,对于可能包含非ASCII字符的所有标头,您不能假定对标头的整个内容进行编码是正常的。例如。 From,To,Cc,Bcc和Reply-To可能包含它们包含的地址的名称,但只能编码名称,而不能编码地址。原因是<encoded-word>
令牌可以仅取代<text>
,<ctext>
和<word>
令牌,并且仅在某些情况下(请参阅§5 of RFC 2047)。
在其他标题中编码非ASCII文本是一个相关但不同的问题。 如果您想了解有关此主题的更多信息,请搜索。如果您没有找到答案,请提出另一个问题并在评论中指出。
答案 2 :(得分:17)
mb_encode_mimeheader()在这里很有用,例如
$subject = mb_encode_mimeheader($subjectText,"UTF-8");
答案 3 :(得分:-2)
使用适当的字符集保存php文件。
就我而言,在Sublime Text中,我使用了以下选项:
档案&gt;使用编码保存&gt;西方(ISO-8859-1)[巴西葡萄牙语]
这样做,您不需要使用任何命令。