52

私のPHPスクリプトはユーザーに電子メールを送信し、電子メールがユーザーのメールボックスに到着すると、件名行($subjecta^£に件名テキストの最後に追加されたような文字が含まれます。これは明らかにエンコーディングの問題です。電子メールメッセージの内容自体は問題ありません。件名だけが壊れています。

私はあちこちを検索しましたが、私の主題を正しくエンコードする方法を見つけることができません。

これは私のヘッダーです。Content-Typeとを使用してcharset=utf-8いる ことに注意してくださいContent-Transfer-Encoding: 8bit

//set all necessary headers
$headers = "From: $sender_name<$from>\n";
$headers .= "Reply-To: $sender_name<$from>\n";
$headers .= "X-Sender: $sender_name<$from>\n";
$headers .= "X-Mailer: PHP4\n"; //mailer
$headers .= "X-Priority: 3\n"; //1 UrgentMessage, 3 Normal
$headers .= "MIME-Version: 1.0\n";
$headers .= "X-MSMail-Priority: High\n";
$headers .= "Importance: 3\n";
$headers .= "Date: $date\n";
$headers .= "Delivered-to: $to\n";
$headers .= "Return-Path: $sender_name<$from>\n";
$headers .= "Envelope-from: $sender_name<$from>\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\n";
4

3 に答える 3

83

更新   より実用的で最新の回答については、Palecの回答をご覧ください。


Content-Typeで指定された文字エンコードは、メッセージ本文の文字エンコードのみを記述し、ヘッダーは記述しません。引用符で囲まれた印刷可能なエンコーディングまたはBase64エンコーディングのいずれかでエンコードされた単語の構文を使用する必要があります。

encoded-word = "=?" charset "?" encoding "?" encoded-text "?="

quoted-printableエンコーディングとBase64エンコーディングにimap_8bit使用できます。base64_encode

"Subject: =?UTF-8?B?".base64_encode($subject)."?="
"Subject: =?UTF-8?Q?".imap_8bit($subject)."?="
于 2010-12-08T16:24:52.167 に答える
63

TL; DR

$preferences = ['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'];
$encoded_subject = iconv_mime_encode('Subject', $subject, $preferences);
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

また

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\r\n", strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

問題と解決策

Content-TypeおよびヘッダーはContent-Transfer-Encoding、メッセージの本文にのみ適用されます。ヘッダーについては、 RFC2047で指定されているエンコーディングを指定するためのメカニズムがあります。

PHP5の時点で存在するSubjectviaをエンコードする必要があります。iconv_mime_encode()

$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);

input-charset文字列のエンコーディングに一致するように変更します$subjectoutput-charsetとして残す必要がありUTF-8ます。PHP 5.4より前では、array()の代わりにを使用して[]ください。

$encoded_subjectは(改行を末尾に付けずに)

Subject: =?UTF-8?B?VmVyeSBsb25nIHRleHQgY29udGFpbmluZyBzcGVjaWFsIGM=?=
 =?UTF-8?B?aGFyYWN0ZXJzIGxpa2UgxJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHA=?=
 =?UTF-8?B?cm9kdWNlcyBzZXZlcmFsIGVuY29kZWQtd29yZHMsIHNwYW5uaW5nIG0=?=
 =?UTF-8?B?dWx0aXBsZSBsaW5lcw==?=

含むため$subject

Very long text containing special characters like ěščřžýáíé<>?=+* produces several encoded-words, spanning multiple lines

それはどのように機能しますか?

このiconv_mime_encode()関数は、テキストを分割し、各部分を個別に<encoded-word>トークンにエンコードして、それらの間の空白を折ります。エンコードされた単語は=?<charset>?<encoding>?<encoded-text>?=次のとおりです。

を介して、またはを介して直接=?CP1250?B?QWhvaiwgc3bsdGU=?=UTF-8文字列Ahoj, světeHello, worldチェコ語)にデコードできます。iconv("CP1250", "UTF-8", base64_decode("QWhvaiwgc3bsdGU="))iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8")

エンコードされた単語へのエンコードはより複雑です。これは、仕様では、エンコードされた単語の各トークンの長さが最大75バイトであり、エンコードされた単語のトークンを含む各行の長さが最大76バイトである必要があるためです(継続行の先頭の空白を含む)。 )。自分でエンコーディングを実装しないでください。あなたが本当に知る必要があるのは、iconv_mime_encode()それがスペックを尊重することだけです。

興味深い関連資料は、ウィキペディアの記事Unicodeと電子メールです。

代替案

基本的なオプションは、制限された文字セットのみを使用することです。ASCIIは動作することが保証されています。ISO Latin 1(ISO-8859-1)は、user2250504が提案したように、エンコードが指定されていない場合にフォールバックとして使用されることが多いため、おそらく機能します。ただし、これらの文字セットは非常に小さいため、必要なすべての文字をエンコードすることはおそらく不可能です。さらに、RFCは、Latin1が機能するかどうかについては何も述べていません。

Paul Normanが答えmb_encode_mimeheader()たように、を使用することもできますが、誤って使用するのは簡単です。

  1. mb_internal_encoding()mbstring関数の内部で使用されるエンコーディングを設定するためにを使用する必要があります。mb_*関数は、入力文字列がこのエンコーディングであると想定しています。注意:の2番目のパラメーターはmb_encode_mimeheader()、入力文字列とは何の関係もありません(マニュアルの内容にかかわらず)。<charset>これは、エンコードされた単語のに対応します(上記の「どのように機能しますか?」を参照)。入力文字列は、BまたはQエンコーディングに渡される前に、内部エンコーディングからこのエンコーディングに再コード化されます。

    PHP 5.6以降、内部エンコーディングの設定は必要ない場合があります。これは、基になるmbstring.internal_encoding構成オプションが廃止されdefault_charset、デフォルトでUTF-8に設定されているオプションが採用されたためです。これは単なるデフォルトであり、コードのデフォルトに依存することは不適切である可能性があることに注意してください。

  2. 入力文字列にはヘッダー名とコロンを含める必要があります。RFCは行の長さに強い制限を課しており、最初の行にも適用する必要があります。$indent別の方法は、5番目のパラメーター( ; 2015年9月現在の最後のパラメーター)をいじることですが、これはさらに便利ではありません。

  3. 実装にはバグがある可能性があります。正しく使用しても、出力が壊れることがあります。少なくともこれは、マニュアルページの多くのコメントが言っていることです。私は何とか問題を見つけることができませんでしたが、エンコードされた単語の実装には注意が必要です。またはに潜在的または実際のバグを見つけた場合は、コメントでお知らせください。mb_encode_mimeheader()iconv_mime_encode()

また、使用することには少なくとも1つの利点がありますmb_encode_mimeheader()。これは、すべてのヘッダーコンテンツを常にエンコードするとは限らないため、スペースが節約され、テキストが人間が読める形式になります。エンコーディングは、非ASCII部分にのみ必要です。iconv_mime_encode()上記の例に類似した出力は次のとおりです。

Subject: Very long text containing special characters like
 =?UTF-8?B?xJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHByb2R1Y2VzIHNldmVyYWwgZW5j?=
 =?UTF-8?B?b2RlZC13b3Jkcywgc3Bhbm5pbmcgbXVsdGlwbGUgbGluZXM=?=

の使用例mb_encode_mimeheader()

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader("Subject: $subject", 'UTF-8');
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

これは、この投稿の上部にあるTL;DRのスニペットの代替です。のためにスペースを予約するだけでなく、Subject: 実際にそこに配置してから削除し、mail()の愚かなインターフェイスで使用できるようにします。

iconvよりもmbstring関数の方が好きな場合は、を使用することをお勧めしますmb_send_mail()。内部で使用しmail()ますが、メッセージの件名と本文を自動的にエンコードします。繰り返しますが、注意して使用してください。

件名以外のヘッダーには別の処理が必要です

非ASCII文字を含む可能性のあるすべてのヘッダーについて、ヘッダーの内容全体をエンコードしても問題ないと想定してはならないことに注意してください。たとえば、From、To、Cc、Bcc、およびReply-Toには、それらに含まれるアドレスの名前を含めることができますが、名前のみをエンコードでき、アドレスはエンコードできません。その理由は、トークンがトークンとトークンだけを<encoded-word>置き換える可能性があり、特定の状況下でのみであるためです(RFC 2047の§5を参照)。<text><ctext><word>

他のヘッダーでの非ASCIIテキストのエンコードは、関連していますが異なる質問です。このトピックについて詳しく知りたい場合は、検索してください。答えが見つからない場合は、別の質問をして、コメントでそれを指摘してください。

于 2014-12-25T14:52:52.413 に答える
20

UTF-8文字列のmb_encode_mimeheader()は、ここで役立ちます。

$subject = mb_encode_mimeheader($subjectText,"UTF-8");
于 2010-12-08T21:31:10.610 に答える