一些使用国家字符并遇到 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
进行编码。
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() 执行折行 (RFC 术语,将超过一定长度的行拆分为多行。长度目前硬编码为 74 个字符) 所使用的 EOL (行结束) 标记。如果没有给出,则回退到 "\r\n"
(CRLF)。
indent
第一行的缩进量 (标头中 string
之前的字符数)。
以 ASCII 表示的 string 的转换版本。
版本 | 描述 |
---|---|
8.0.0 |
charset 和 transfer_encoding 现在可以为空。 |
示例 #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-?=" <[email protected]>
注意:
此函数并非设计用于在更高级别的上下文断点 (单词边界等) 处断行。此行为可能会在原始字符串中添加意外的空格,使字符串变得混乱。
一些使用国家字符并遇到 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<[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