正如其他人所說,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;
}