正如其他人所说,mb_encode_mimeheader() 在使用 JIS(ISO-2022-JP) 编码时似乎存在错误。指示多字节字符开始和结束的标记仅在编码开始之前和之后插入。
我们需要在*所有*编码文本的开始和结束处使用标记。
# PHP 4.3.2
因此,我们需要一些解决方法。
"gordon@kanazawa-gu.ac.jp" 发布的文章似乎可以很好地工作,但事实并非如此。JIS 需要一些特殊的标记,而 base64_encode 不会输出这些标记,因此该代码无法使用。
"RE: N03L in Japan" 发布的文章,简单地将单词每 10 个字符分割一次,在大多数情况下都足够好,但还存在一些问题。当某些分割的部分以 ascii 字符开头时,会插入 1 个额外的空格。
# RFC2047 只规定在 76 个单词内,更短没有问题。
此外,尽管这种情况非常罕见,但当我们将 "=?charset?" 作为字面字符串使用时,我们必须对其进行转义。
# 有些邮件程序实际上可以在不转义的情况下发送此邮件,并且标头将被破坏。:(
现在,以下函数代码虽然不太智能,但可能更准确。我仅在 ISO-2022-JP 上进行了测试,仅在自定义的 phpBB2.0.5 中,仅在某些情况下进行了测试。
据我测试,此代码运行良好,但我并不确定。
# $str: 源文本
# $indent: 例如,对于 "Subject: " 标头,给出 9,第一行将短于 76-1-9=66
# $encoding: 源文本编码
# $mail_encoding: $str 将在此之前转换为 base64 编码
function encode_mimeheader($str, $indent = 0, $encoding = 'utf-8', $mail_encoding = 'iso-2022-jp')
{
$start_delimiter = strtoupper("=?$mail_encoding?B?");
$start_pattern = strtoupper("=\\?$mail_encoding\\?B\\?");
$end_delimiter = '?=';
$str = mb_convert_encoding($str, $mail_encoding, $encoding);
$length = mb_strlen($str, $mail_encoding);
$max_part_length = 20; // 在大多数情况下足够短(您可以更改此默认值)
for ($i=0, $index=0; $index<$length; $i++) {
$part_length = $max_part_length;
$s = mb_substr($str, $index, $part_length, $mail_encoding);
// 用于字面使用的起始分隔符的解决方法(没有以下内容,主题可能会中断)
// 注意:mb_encode_mimeheader() 不会编码包括起始分隔符在内的 ASCII 字符
if (preg_match('/^' . $start_pattern . '/i', $s))
{
$lines[$i] = $start_delimiter . base64_encode($start_delimiter) . "?=";
$index += strlen($start_delimiter);
continue;
}
$lines[$i] = mb_encode_mimeheader($s, $mail_encoding);
while (strlen($lines[$i]) > 76-1 - ($i?0:$indent)) { // 每行最大长度 - 第一个空格 - 缩进(仅第一行)
$part_length = floor($part_length * (76-1 - ($i?0:$indent)) / strlen($lines[$i])); // 至少减少 1
$s = mb_substr($str, $index, $part_length, $mail_encoding);
$lines[$i] = mb_encode_mimeheader($s, $mail_encoding);
}
// 用于以 ASCII 字符开头新行的解决方法(没有以下内容,可能会截断 1 个空格)
// 注意:mb_encode_mimeheader() 在遇到多字节字符后开始编码
if ($i > 0 && !preg_match('/^' . $start_pattern . '/i', $lines[$i]))
{
$p = strpos($lines[$i], $start_delimiter); // 从不为 0(未找到时为 false)
$p = $p ? $p : strlen($lines[$i]);
$lines[$i] = $start_delimiter . base64_encode(substr($lines[$i], 0, $p)) . "?=";
$part_length = $p;
}
$index += $part_length;
}
$str = join("\r\n ", $lines); // RFC 2047,822 说换行符必须是 ^\r\n,而不仅仅是 \n
return $str;
}