daz*_*166 51 php encoding mime email-headers
我的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";
Run Code Online (Sandbox Code Playgroud)
Gum*_*mbo 79
更新 如需更实用和最新的答案,请查看Palec的答案.
Content-Type中指定的字符编码仅描述邮件正文的字符编码,但不描述标题.您需要将编码字语法与quoted-printable编码或Base64编码一起使用:
encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
Run Code Online (Sandbox Code Playgroud)
您可以使用imap_8bit
的引用可打印编码base64_encode
为Base64编码:
"Subject: =?UTF-8?B?".base64_encode($subject)."?="
"Subject: =?UTF-8?Q?".imap_8bit($subject)."?="
Run Code Online (Sandbox Code Playgroud)
Pal*_*lec 56
$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);
Run Code Online (Sandbox Code Playgroud)
要么
mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\r\n", strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);
Run Code Online (Sandbox Code Playgroud)
在Content-Type
和Content-Transfer-Encoding
头仅适用于您的邮件的正文.对于标头,有一种机制用于指定RFC 2047中指定的编码.
你应该编码你的Subject
via iconv_mime_encode()
,从PHP 5开始存在:
$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);
Run Code Online (Sandbox Code Playgroud)
更改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==?=
Run Code Online (Sandbox Code Playgroud)
对于$subject
包含:
Very long text containing special characters like ?š??žýáíé<>?=+* produces several encoded-words, spanning multiple lines
Run Code Online (Sandbox Code Playgroud)
该iconv_mime_encode()
函数拆分文本,将每个部分分别编码为一个<encoded-word>
令牌并折叠它们之间的空白.编码的单词是=?<charset>?<encoding>?<encoded-text>?=
在哪里:
<encoding>
是B
(对于Base 64 - 请参阅base64_encode()
)或Q
(对于Quoted-printable - 请参阅quoted_printable_encode()
),<encoded-text>
是字符串编码的<encoding>
,<charset>
在解码后有字符集.您可以通过或直接通过解码=?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()
尊重规范.
有趣的相关阅读是维基百科文章Unicode和电子邮件.
一个基本选项是仅使用一组受限制的字符.ASCII保证可以工作.ISO Latin 8(ISO-8859-1),如用户2250504建议的那样,也可能会起作用,因为当没有指定编码时,它通常用作后备.但是这些字符集非常小,你可能无法编码你想要的所有字符.此外,RFC并未说明拉丁语1是否应该起作用.
你可以使用mb_encode_mimeheader()
,正如保罗诺曼回答的那样,但很容易错误地使用它.
您必须使用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=?=
Run Code Online (Sandbox Code Playgroud)
用法示例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);
Run Code Online (Sandbox Code Playgroud)
这是TL中的片段的替代; DR在此帖子之上.Subject:
它实际上将其放在那里然后将其删除,以便能够将其与mail()
愚蠢的界面一起使用,而不仅仅是保留空间.
如果你喜欢mbstring函数比iconv函数更好,你可能想要使用mb_send_mail()
.它在mail()
内部使用,但自动编码消息的主题和正文.再次,小心使用.
请注意,对于可能包含非ASCII字符的所有标头,您不能认为对标头的整个内容进行编码是正常的.例如From,To,Cc,Bcc和Reply-To可能包含它们包含的地址的名称,但只能编码名称,而不能编码地址.其原因是,<encoded-word>
令牌可以只更换<text>
,<ctext>
和<word>
令牌,只有在特定情况下(参见RFC 2047的§5).
在其他标头中编码非ASCII文本是一个相关但不同的问题.如果您想了解有关此主题的更多信息,请搜索.如果您找不到答案,请提出另一个问题并在评论中指出.
Pau*_*man 18
UTF-8字符串的mb_encode_mimeheader()在这里很有用,例如
$subject = mb_encode_mimeheader($subjectText,"UTF-8");
Run Code Online (Sandbox Code Playgroud)