PHP 大会日本 2024

mb_send_mail

(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)

mb_send_mail发送编码邮件

描述

mb_send_mail(
    字符串 $to,
    字符串 $subject,
    字符串 $message,
    数组|字符串 $additional_headers = [],
    ?字符串 $additional_params = null
): 布尔值

发送电子邮件。标头和消息根据 mb_language() 设置进行转换和编码。它是 mail() 的包装函数,因此请参阅 mail() 以获取详细信息。

参数

to

发送到的邮件地址。可以通过在 to 中的每个地址之间放置逗号来指定多个收件人。此参数不会自动编码。

subject

邮件的主题。

message

邮件的内容。

additional_headers (可选)

字符串数组,插入到电子邮件标头的末尾。

这通常用于添加额外的标头(发件人、抄送和密送)。多个额外的标头应以 CRLF (\r\n) 分隔。验证参数,以防止攻击者注入不需要的标头。

如果传递了一个 数组,则其键是标头名称,其值是相应的标头值。

注意:

发送邮件时,邮件**必须**包含 From 标头。这可以通过 additional_headers 参数设置,或者可以在 php.ini 中设置默认值。

如果不这样做,将导致类似于 Warning: mail(): "sendmail_from" not set in php.ini or custom "From:" header missing 的错误消息。 From 标头还在 Windows 下设置 Return-Path

注意:

如果未收到消息,请尝试仅使用 LF (\n)。某些 Unix 邮件传输代理(最著名的是 » qmail)会自动将 LF 替换为 CRLF(如果使用 CRLF,则会导致 CR 加倍)。这应该是最后的手段,因为它不符合 » RFC 2822

additional_params

additional_params 是 MTA 命令行参数。在使用 sendmail 设置正确的 Return-Path 标头时很有用。

此参数在内部由 escapeshellcmd() 转义,以防止命令执行。 escapeshellcmd() 防止命令执行,但允许添加其他参数。出于安全原因,应验证此参数。

由于 escapeshellcmd() 是自动应用的,因此某些被互联网 RFC 允许用作电子邮件地址的字符无法使用。需要使用这些字符的程序 mail() 无法使用。

Web 服务器运行的用户应作为受信任的用户添加到 sendmail 配置中,以防止在使用此方法设置信封发件人 (-f) 时将“X-Warning”标头添加到邮件中。对于 sendmail 用户,此文件是 /etc/mail/trusted-users

返回值

成功时返回 true,失败时返回 false

变更日志

版本 描述
8.0.0 additional_params 现在可以为空。
7.2.0 additional_headers 参数现在也接受 数组

参见

添加注释

用户贡献的注释 15 个注释

gordon at kanazawa-gu dot ac dot jp
21 年前
如果您的服务器未启用 mb_send_mail(),但您希望在电子邮件的主题或名称标头中使用非 ASCII(多字节)字符,您可以使用以下类似内容

$charset = "iso-2202-jp"; // 日语
$to = encode("日语姓名 01", $charset) . " <[email protected]>";
$from = encode("日语姓名 02", $charset) . " <[email protected]>";
$subject = encode("日语文本", $charset);
$message = "不需要编码";
mail($to, $subject, $message, $from);

function encode($in_str, $charset) {
$out_str = $in_str;
if ($out_str && $charset) {

// 定义开始分隔符、结束分隔符和空格符
$end = "?=";
$start = "=?" . $charset . "?B?";
$spacer = $end . "\r\n " . $start;

// 确定编码文本在块中的长度
// 并确保长度为偶数
$length = 75 - strlen($start) - strlen($end);
$length = floor($length/2) * 2;

// 编码字符串并将其拆分为块
// 每个块后面都有空格符
$out_str = base64_encode($out_str);
$out_str = chunk_split($out_str, $length, $spacer);

// 删除尾随空格符并
// 添加开始和结束分隔符
$spacer = preg_quote($spacer);
$out_str = preg_replace("/" . $spacer . "$/", "", $out_str);
$out_str = $start . $out_str . $end;
}
return $out_str;
}
// 有关消息标头扩展的详细信息
// 有关非 ASCII 文本,请参阅...
// http://www.faqs.org/rfcs/rfc2047.html
dynamis at skillup dot jp
21 年前
# 自我修复...
我在前一天发布了“encode_mimeheader”解决方法。但我发现代码依赖于平台。:(

在某些平台上,标头(在标头拆分为两行或多行之后)将出现在正文内容中。
原因是“某些平台会自动为您完成从 \n 到 \r\n 的转换”。
# 请参阅“leon at ietsmet dot nl”关于邮件函数的帖子(2003 年 4 月 23 日)。

现在,您只需将粘合字符串从“\r\n ”更改为“\n ”即可。

function encode_mimeheader($str, $indent = 0, $encoding = 'utf-8', $mail_encoding = 'iso-2022-jp')
{
..... 省略(都一样).....
$str = join("\r\n ", $lines); // RFC 2047,822 说换行符必须是 ^\r\n,而不仅仅是 \n
return $str;
}

在某些平台上应该在下面...

function encode_mimeheader($str, $indent = 0, $encoding = 'utf-8', $mail_encoding = 'iso-2022-jp')
{
..... 省略(都一样).....
$str = join("\n ", $lines);
// 尽管 RFC 2047,822 说换行符必须是 \r\n,而不仅仅是 \n,
// 但在我的平台上,会自动替换(接受 \n 和 \n\r\n -> \n\n)...
return $str;
}

# 注意:在两种代码中,换行符后都有一个空格符(粘合字符串**不是**仅“\r\n”或“\n”)。
# 此空格符仅在编码词之间被忽略。

参考效率

考虑到效率,我的 encode_mimeheader 可能必须确定在确定循环中不使用 mb_encode_mimeheader 的情况下在哪里将字符串拆分为行。但该循环只会重复一两次,并且此函数通常用于处理不太长的字符串。因此,在大多数情况下,这种损失并不关键。
如果您只使用一种编码,您可以根据编码的定义逐个轻松检查 ASCII 或多字节字符,并确定分割点(这快得多),但是当您必须处理所有编码时,这并不容易。至少,我不想碰它。:p
要接受*任何*编码(编码名称将作为参数传递),您可以使用此代码。这是因为我发布了这样的非高效代码。
# 尽管我不确定该代码是否真的能与所有编码正常工作...
RE: N03L in Japan
21 年前
我必须在邮件主题和正文中使用日语半角(单字节片假名在(S)JIS 中),
所以我尝试了日本 NO3L 提到的方法,但发现存在一个大问题。
在这种方法中,mb_encode_mimeheader 会从第二行开始的编码字符串头部丢弃转义序列。
如果你在邮件主题中使用长字符,并且包含多字节字符串,它可能会崩溃。
因此,如果你想在邮件主题中使用日语半角字符,可以尝试以下方法。
将主题字符编码在一行中,不仅仅是 76 个字符,所以它不符合 RFC 标准,但它可以工作。

$intSubjectLength = mb_strlen($strSubject);
$intSeparateLength = 10;
for ($i=0; $i<ceil($intSubjectLength / $intSeparateLength); $i++) {
$arrSeparatedSubject[$i] = mb_substr($strSubject, $intIndex, $intSeparateLength);
$arrSeparatedSubject[$i] = mb_encode_mimeheader(mb_convert_encoding($arrSeparatedSubject[$i], "JIS", "EUC-JP"));
$intIndex = $intIndex + $intSeparateLength;
}
$strSubject = join("\n ", $arrSeparatedSubject);
[email protected]
20 年前
确保如果使用表单输入邮件,表单页面具有正确的编码,例如,如果我想发送一封日语邮件,通过填写表单,表单页面需要在头部包含以下内容
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=SHIFT-JIS">
Sohel Taslim
17 年前
简单易用……
发送包含日语内容的电子邮件。

<?php

/**
* @name : sendMail
* @author Taslim Mazumder Sohel
* @[email protected]
* 发送邮件的函数
* 包含日语邮件正文、主题、发件人姓名。
*
* @param String $to : 收件人邮件地址。
* @param String $subject : 邮件主题。
* @param int $body : 邮件正文文本。
* @param array $from_email : 发件人邮件地址。
* @param array $from_name : 发件人姓名。
*
*/
function sendMail($to, $subject, $body, $from_email,$from_name)
{
$headers = "MIME-Version: 1.0 \n" ;
$headers .= "From: " .
"".mb_encode_mimeheader (mb_convert_encoding($from_name,"ISO-2022-JP","AUTO")) ."" .
"<".$from_email."> \n";
$headers .= "Reply-To: " .
"".mb_encode_mimeheader (mb_convert_encoding($from_name,"ISO-2022-JP","AUTO")) ."" .
"<".$from_email."> \n";


$headers .= "Content-Type: text/plain;charset=ISO-2022-JP \n";


/* 将正文转换为与上面 Content-Type 标头中指定的编码相同
*/

$body = mb_convert_encoding($body, "ISO-2022-JP","AUTO");

/* 邮件,可选参数。 */
$sendmail_params = "-f$from_email";

mb_language("ja");
$subject = mb_convert_encoding($subject, "ISO-2022-JP","AUTO");
$subject = mb_encode_mimeheader($subject);

$result = mail($to, $subject, $body, $headers, $sendmail_params);

return
$result;
}

?>
[email protected]
21 年前
我一直在尝试发送日语邮件,并且花了很长时间才使其正常工作。我最终偶然发现了 mb_send_mail(),它完成了我的所有需求,*除了*它只能发送纯文本邮件……发送 HTML 内容是不可能的。

我最终写出了这个代码。我发布它以防有人发现它有用。

function send_japanese_mail($to, $subject, $body, $from, $from_email, $is_html_content=false) {

$headers = "MIME-Version: 1.0\\n" ;
$headers .= "From: $from <$from_email>\\n";
$headers .= "Reply-To: $from <$from_email>\\n";

/* 如果正文是 HTML 或纯文本,则相应地设置 Content-Type 标头 */

if ($is_html_content) {
$headers .= "Content-Type: text/html;charset=ISO-2022-JP\\n";
/* 将所有换行符转换为 BR 标签 */
$body = nl2br($body);
}
else {
$headers .= "Content-Type: text/plain;charset=ISO-2022-JP\\n";
}

/* 需要将正文转换为与上面 Content-Type 标头中指定的编码相同 */

$body = mb_convert_encoding($body, "ISO-2022-JP","AUTO");

/* 设置任何 sendmail 参数,可选... */
$sendmail_params = "-f$from_email";

/*
主题实际上是一个“标头”,如果包含非 ASCII 字符,可能会被损坏。
因此,我们需要将主题转换为仅包含 ASCII 字符的内容。
首先,我们将主题转换为与正文相同的编码,然后使用 mb_encode_mimeheader() 使主题

行全部为 ASCII 字符。
首先,我们将主题转换为与正文相同的编码,然后使用 mb_encode_mimeheader() 使主题
行全部为 ASCII 字符。
*/

mb_language("ja");
$subject = mb_convert_encoding($subject, "ISO-2022-JP","AUTO");
$subject = mb_encode_mimeheader($subject);

mail($to, $subject, $body, $headers, $sendmail_params);
}
[email protected]
22 年前
在 Linux 上使用 PHP 4.2.2 测试:请注意,如果使用 Unicode (mb_language("uni")) 并尝试使用 mb_send_mail() 发送邮件,则需要对邮件正文进行 base64_encode() 编码 - mb_send_mail() 不会为您执行此操作。但是,它确实会发出正确的邮件标头,因此您无需担心这一点。

另请注意,mb_language() 和 mb_send_mail() 都无法将您的邮件转换为 UTF-8。*您*需要提供 UTF-8 编码(和 base64 编码)的邮件,然后 mb_send_mail() 将发出正确的邮件标头。

以下是如何使用 mb_send_mail() 发送 UTF-8 编码邮件的示例

mb_language("uni");
$body = chunk_split(base64_encode("国际字符"));
mb_send_mail("[email protected]", "主题", $body);

如果接收邮件客户端正确支持 UTF-8,则可以发送包含各种字符的邮件(例如,可以在同一封邮件中发送泰语、中文和丹麦语字符)。但是,并非所有邮件客户端都支持 UTF-8。在撰写本文时,一些比较流行的 Windows 邮件客户端(如 Eudora 和 Pegasus Mail)不支持。有一些邮件客户端可以正常支持 UTF-8。其中包括 Outlook Express、KMail、Mozilla、Netscape 6/7、Sylpheed、Evolution 等。
匿名
14 年前
如果您在使用 mb_send_mail 发送 utf-8 邮件时遇到问题,您需要了解以下几点(在找到正确的 php 代码之前,我在网上花费了数小时)

- 您必须对发件人姓名和收件人姓名使用 mb_encode_mimeheader 函数(在姓名上,而不是在电子邮件上!)。
- 主题和消息会自动编码。
- 添加 - 至少 - 以下标头

$headers = "Mime-Version: 1.0\n";
$headers .= "Content-Type: text/plain;charset=UTF-8\n";
$headers .= "From: $sender";

- 最后但并非最不重要的是,请注意内部编码,mb_encode_mimeheader 函数需要此编码才能正确编码。我必须将内部编码设置为 UTF-8 才能使其正常工作

mb_internal_encoding("UTF-8");
[email protected]
11 年前
在使用其他评论中提到的正确标签(mb_internal_encoding、Content-Type 等)时……

mb_send_mail 将正确编码纯文本电子邮件中主题和消息中的特殊字符

mb_send_mail 不会正确编码多部分邮件(文本+html)主题中的特殊字符
[email protected]
21 年前
还有另一种编码日语以发送电子邮件的方法,无需担心 mb_functions

http://www.spencernetwork.org/ 下载 PHP Jcode
并使用 JcodeConvert 函数和 base64_encode 函数对电子邮件标头和主题进行编码。

$email_to = "[email protected]";

$_POST["name"] = strip_tags(trim($_POST["name"]));
$name = "=?ISO-2022-JP?B?". base64_encode(JcodeConvert($_POST["name"], 2, 3)). "?=";
$_POST["email"] = strip_tags(trim($_POST["email"]));
$_POST["subject"] = strip_tags(trim($_POST["subject"]));
$email_subject = "=?ISO-2022-JP?B?". base64_encode(JcodeConvert($_POST["subject"], 2, 3)). "?=";
$_POST["body"] = strip_tags(trim($_POST["body"]));

$email_header = "From: $name <". $_POST["email"] .">\n";
$email_header .= "Reply-To: ". $_POST["email"] ."\n";
$email_header .= "Content-transfer-encoding: 7bit\n";
$email_header .= "Content-type: text/plain; charset=\"iso-2022-jp\"\n\n";

$email_body = "blah blah blah.\n"

mail($email_to, $email_subject, $email_body, $email_header);
[email protected]
21 年前
正如其他人所说,mb_encode_mimeheader() 在使用 JIS(ISO-2022-JP) 编码时似乎存在错误。指示多字节字符开始和结束的标记仅在编码开始之前和之后插入。
我们需要在*所有*编码文本的开始和结束处使用标记。
# PHP 4.3.2

因此,我们需要一些解决方法。

"[email protected]" 发布的文章似乎可以很好地工作,但事实并非如此。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;
}
noting@nowhere
17 年前
是的,据我理解,该函数使用 base64 编码整个邮件正文,因此它可以用于发送包含附件的邮件。
匿名用户
17 年前
小心,如果您尝试发送多部分电子邮件,mb_send_mail 会出错。
Ran Hamada ( rhamada at sdcj dot co dot jp )
21 年前
您不能将 mb_encode_mimeheader() 与 mb_convert_encoding() 一起使用来创建主题,如下所示。这会导致某些字符串出现乱码。
mb_encode_mimeheader( mb_convert_encoding($strMailSubj, "JIS", "EUC-JP") )

将 mb_internal_encoding() 设置为 *主题* 的编码并调用 mb_encode_mimeheader。

示例)

$__lang = mb_language();
$__enc = mb_internal_encoding();
mb_language("Japanese");
mb_internal_encoding( mb_detect_encoding($subject) );
#mb_internal_encoding( "EUC-JP" ); #仅在您知道 $subject 的编码时执行
mail($to,
mb_encode_mimeheader($subject),
mb_convert_encoding($msg,"JIS","AUTO"),$header);
mb_internal_encoding( $__enc );
mb_language($__lang);
N03L in Japan
21 年前
首先,除非您想编写 xhtml,否则不要在较新的 php 版本中使用 nl2br...您将获得 <br /> 标记。

其次,您应该使用 JIS,而不是 ISO-2022-JP 作为转换到的编码。如果您使用 ISO...mb_encode_mimeheader 似乎会失败。无论如何,它们是相同的字符集。

现在我为什么要写这篇文章。我刚刚发现 mb_send_mail 和 mb_encode_mimeheader 完全不支持半角((S)JIS 中的单字节假名)。如果您正在为手机市场制作应用程序,这将无法实现。因此,撤消您对邮件函数的重载,并使用常规邮件并像这样自己转换所有内容。这就是我们所做的。它似乎对我们有效。您的里程可能会有所不同。

$strMBS = mb_convert_encoding($strMailBodySend, "JIS", "EUC-JP");

$strMS = mb_encode_mimeheader( mb_convert_encoding($strMailSubj, "JIS", "EUC-JP") );

mail($strToMail, $strMS, $strMBS, $strFrom);
To Top