一些使用国家字符并存在 UTF-8 问题的解决方案,例如在邮件主题中。在使用 mb_encode_mimeheader 与 UTF-8 之前,请设置 mb_internal_encoding('UTF-8')。
(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)
mb_encode_mimeheader — 对 MIME 头进行编码
$string
,$charset
= null
,$transfer_encoding
= null
,$newline
= "\r\n",$indent
= 0使用 MIME 头编码方案对给定的 字符串 string
进行编码。
string
被编码的 字符串。其编码应与 mb_internal_encoding() 相同。
charset
charset
指定 string
所表示的字符集名称。默认值由当前 NLS 设置 (mbstring.language
) 确定。
transfer_encoding
transfer_encoding
指定 MIME 编码方案。它应该是 "B"
(Base64) 或 "Q"
(Quoted-Printable)。如果没有给出,则回退到 "B"
。
newline
newline
指定 mb_encode_mimeheader() 执行行折叠时使用的 EOL(行尾)标记(一个 » RFC 术语,将长于一定长度的行分成多行的行为。当前长度硬编码为 74 个字符)。如果没有给出,则回退到 "\r\n"
(CRLF)。
indent
第一行的缩进(string
之前的标题中的字符数)。
以 ASCII 表示的 字符串 的转换版本。
版本 | 描述 |
---|---|
8.3.0 |
使用 Quoted-Printable 编码进行编码时,不再丢弃 NUL (0) 字节,而是将其编码为 =00 。 |
8.0.0 |
charset 和 transfer_encoding 现在可以为空。 |
示例 #1 mb_encode_mimeheader() 示例
<?php
$name = "太郎"; // 日文汉字
$mbox = "kru";
$doma = "gtinn.mon";
$addr = '"' . addcslashes(mb_encode_mimeheader($name, "UTF-7", "Q"), '"') . " <" . $mbox . "@" . $doma . ">";
echo $addr;
?>
以上示例将输出
"=?UTF-7?Q?+WSqQzg-?=" <[email protected]>
注意:
此函数并非设计为在更高级别的上下文断点(单词边界等)处换行。此行为可能会使原始字符串中出现意外的空格。
一些使用国家字符并存在 UTF-8 问题的解决方案,例如在邮件主题中。在使用 mb_encode_mimeheader 与 UTF-8 之前,请设置 mb_internal_encoding('UTF-8')。
确实,函数已损坏(PHP5.1,使用 pl_PL 字符集从 UTF-8 编码)。以下是所提出的 _mb_mime_encode 版本,速度提高了约 15%。它还具有类似于其他 mb_* 函数的标头,并且不会触发任何错误/警告/通知。
<?php
function mb_mime_header($string, $encoding=null, $linefeed="\r\n") {
if(!$encoding) $encoding = mb_internal_encoding();
$encoded = '';
while($length = mb_strlen($string)) {
$encoded .= "=?$encoding?B?"
. base64_encode(mb_substr($string,0,24,$encoding))
. "?=$linefeed";
$string = mb_substr($string,24,$length,$encoding);
}
return $encoded;
}
?>
请先阅读:http://bugs.php.net/bug.php?id=23192 因为 mb_encode_mimeheaders 有 BUG!
针对 ISO-2022-JP 过长主题的多字节损坏错误的解决方法
$pos=0;
$split=36; // 超过 36 个单字节字符后,如果出现 MB,则会损坏
while ($pos<mb_strlen($string,$encoding))
{
$output=mb_strimwidth($string,$pos,$split,"",$encoding);
$pos+=mb_strlen($output,$encoding);
$_string.=(($_string)?' ':'').mb_encode_mimeheader($output,$encoding);
}
$string=$_string;
不是最好的,但它有效
我找不到 PHP 函数来 MIME 编码电子邮件地址的名称。
输入 = "Karl Müller<[email protected]>"
输出 = "Karl%20M%FCller<[email protected]>"
我自己写了它
<?php
// 需要对电子邮件地址中的名称进行编码
// 将 " " 替换为 "%20"
// 将 "ü" 替换为 "%FC"
// 将 "%" 替换为 "%25" 等等....
// 使用 "%" 作为 MIME 分隔符
// 使用 "=" 作为 Quoted Printable 分隔符
// 输入字符串必须是 UTF8 编码
public static function EncodeMime($Text, $Delimiter)
{
$Text = utf8_decode($Text);
$Len = strlen($Text);
$Out = "";
for ($i=0; $i<$Len; $i++)
{
$Chr = substr($Text, $i, 1);
$Asc = ord($Chr);
if ($Asc > 0x255) // 不允许使用 Unicode
{
$Out .= "?";
}
else if ($Chr == " " || $Chr == $Delimiter || $Asc > 127)
{
$Out .= $Delimiter . strtoupper(bin2hex($Chr));
}
else $Out .= $Chr;
}
return $Out;
}
?>
mb_encode_mimeheader() 依赖于正确的 mbstring.internal_encoding 设置。它尝试将 $str 从内部编码转换为 $charset。如果您忽略 mbstring 内部编码,即使 $str 字符集与 $charset 匹配,函数也可能会错误地编码字符串。
我的第一篇文章大约在 2003 年,mb_mime_header 仍然存在问题。它*不能*用于较长的主题,并且除了日语之外,大多数情况下都无法使用。
iwakura at junx dot org 对我来说也不起作用,它也产生了一些垃圾。
我更新了我的旧函数(我在 2003 年发布的函数),并使用 UTF-8、ISO-2022-JP(日语)、GB2312(简体中文)和 EUC-KR(韩语)中的超长主题对其进行了测试,并在 Thunderbird、mail.app、Outlook 等中获得了可读的结果。
<?php
function _mb_mime_encode($string, $encoding)
{
$pos = 0;
// 如果在 36 个单字节字符之后出现 MB,则会中断
// 但我将其缩减到 24,以保持每行 100% < 76 个字符
$split = 24;
while ($pos < mb_strlen($string, $encoding))
{
$output = mb_strimwidth($string, $pos, $split, "", $encoding);
$pos += mb_strlen($output, $encoding);
$_string_encoded = "=?".$encoding."?B?".base64_encode($output)."?=";
if ($_string)
$_string .= "\r\n";
$_string .= $_string_encoded;
}
$string = $_string;
return $string;
}
?>
如果 mb_ 版本在 MIME-B 模式下对您不起作用
function encode_mimeheader($string, $charset=null, $linefeed="\r\n") {
if (!$charset)
$charset = mb_internal_encoding();
$start = "=?$charset?B?";
$end = "?=";
$encoded = '';
/* 每行长度必须 <= 75,包括 $start 和 $end */
$length = 75 - strlen($start) - strlen($end);
/* 平均多字节比率 */
$ratio = mb_strlen($string, $charset) / strlen($string);
/* Base64 的比率为 4:3 */
$magic = $avglength = floor(3 * $length * $ratio / 4);
for ($i=0; $i <= mb_strlen($string, $charset); $i+=$magic) {
$magic = $avglength;
$offset = 0;
/* 重新计算每行的 magic 以确保 100% */
do {
$magic -= $offset;
$chunk = mb_substr($string, $i, $magic, $charset);
$chunk = base64_encode($chunk);
$offset++;
} while (strlen($chunk) > $length);
if ($chunk)
$encoded .= ' '.$start.$chunk.$end.$linefeed;
}
/* 去掉第一个空格和最后一个换行符 */
$encoded = substr($encoded, 1, -strlen($linefeed));
return $encoded;
}
在使用非美国 ASCII 字符的国家/地区,这是一个非常好的发送邮件示例
mb_internal_encoding('iso-8859-2');
setlocale(LC_CTYPE, 'hu_HU');
function encode($str,$charset){
$str=mb_encode_mimeheader(trim($str),$charset, 'Q', "\n\t");
return $str;
}
print encode('the text with spec. chars: ő Ű Ő ű, ?','iso-8859-2');
它创建了一个 7 位字符串
我认为 mb_encode_mimeheader 仍然存在错误。这是一个示例代码
function mb_encode_mimeheader2($string, $encoding = "ISO-2022-JP") {
$string_array = array();
$pos = 0;
$row = 0;
$mode = 0;
while ($pos < mb_strlen($string)) {
$word = mb_strimwidth($string, $pos, 1);
if (!$word) {
$word = mb_strimwidth($string, $pos, 2);
}
if (mb_ereg_match("[ -~]", $word)) { // ascii
if ($mode != 1) {
$row++;
$mode = 1;
$string_array[$row] = NULL;
}
} else { // 多字节
if ($mode != 2) {
$row++;
$mode = 2;
$string_array[$row] = NULL;
}
}
$string_array[$row] .= $word;
$pos++;
}
//echo "<pre>";
//print_r($string_array);
//echo "</pre>";
foreach ($string_array as $key => $value) {
$value = mb_convert_encoding($value, $encoding);
$string_array[$key] = mb_encode_mimeheader($value, $encoding);
}
//echo "<pre>";
//print_r($string_array);
//echo "</pre>";
return implode("", $string_array);
}
不是最好的,但它有效
至少对于 Q 编码,此函数是不安全的,并且不能正确编码。显示为 RFC2047 序列的原始字符被原样保留。
例如
mb_encode_mimeheader( '=?iso-8859-1?q?this=20is=20some=20text?=' );
返回 '=?iso-8859-1?q?this=20is=20some=20text?='
完全相同的字符串,显然不是源字符串的编码。也就是说,mb_encode_mimeheader 没有进行任何类型的转义。
也就是说,以下条件并不总是成立的
mb_decode_mimeheader( mb_encode_mimeheader( $text ) ) == $text