一些使用國家字元並在郵件主旨中使用 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
指定 EOL(行尾)標記,mb_encode_mimeheader() 使用此標記執行換行(» RFC 術語,將長度超過特定長度的行分割成多行的動作。長度目前硬式編碼為 74 個字元)。如果未指定,則會回退到 "\r\n"
(CRLF)。
indent
第一行的縮排(string
前標頭中的字元數)。
以 ASCII 表示的 字串 轉換版本。
版本 | 描述 |
---|---|
8.3.0 |
使用 Quoted-Printable 編碼時,NUL (0) 位元組不再被捨棄,而是編碼為 =00 。 |
8.0.0 |
charset 和 transfer_encoding 現在可以為 null。 |
範例 #1 mb_encode_mimeheader() 範例
<?php
$name = "太郎"; // kanji
$mbox = "kru";
$doma = "gtinn.mon";
$addr = '"' . addcslashes(mb_encode_mimeheader($name, "UTF-7", "Q"), '"') . '" <' . $mbox . "@" . $doma . ">";
echo $addr;
?>
上面的範例會輸出
"=?UTF-7?Q?+WSqQzg-?=" <kru@gtinn.mon>
注意:
此函式並非設計為在高層次上下文斷點(單字邊界等)處斷行。此行為可能會使原始字串與非預期的空格混雜。
一些使用國家字元並在郵件主旨中使用 UTF-8 時遇到問題的解決方案。在使用 mb_encode_mimeheader 與 UTF-8 之前,請設定 mb_internal_encoding('UTF-8')。
確實,函式已損壞(PHP5.1,來自 UTF-8 具有 pl_PL 字元集的編碼)。以下是所建議的 _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 有錯誤!
針對 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<kmueller@gmx.de>"
輸出 = "Karl%20M%FCller<kmueller@gmx.de>"
我自己寫的
<?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 個單位元組字元之後出現多位元組字元,它就會壞掉
// 但我將其修剪為 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