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) . " <to@email.com>";
$from = encode("日語姓名 02", $charset) . " <from@email.com>";
$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);
bredfern@calarts.edu
20 年前
確保如果使用表單輸入郵件,表單頁面具有正確的編碼,例如,如果我想傳送一封日語郵件,透過填寫表單,表單頁面需要在頭部包含以下內容
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=SHIFT-JIS">
Sohel Taslim
17 年前
簡單易用……
傳送包含日語內容的電子郵件。

<?php

/**
* @name : sendMail
* @author Taslim Mazumder Sohel
* @mailsohel62@yahoo.com
* 傳送郵件的函式
* 包含日語郵件正文、主題、發件人姓名。
*
* @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;
}

?>
jc@mega-bucks.co.jp
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);
}
no.email.address@example.com
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("someone@example.com", "主題", $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");
contact-form@contactrobot.com
11 年前
在使用其他評論中提到的正確標籤(mb_internal_encoding、Content-Type 等)時……

mb_send_mail 將正確編碼純文字電子郵件中主題和訊息中的特殊字元

mb_send_mail 不會正確編碼多部分郵件(文字+html)主題中的特殊字元
phpguru@hotmail.com
21 年前
還有另一種編碼日語以傳送電子郵件的方法,無需擔心 mb_functions

http://www.spencernetwork.org/ 下載 PHP Jcode
並使用 JcodeConvert 函式和 base64_encode 函式對電子郵件標頭和主題進行編碼。

$email_to = "blah@blah.com";

$_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);
dynamis@skillup.jp
21 年前
正如其他人所說,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;
}
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