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の時点で存在するSubject
viaをエンコードする必要があります。iconv_mime_encode()
$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);
input-charset
文字列のエンコーディングに一致するように変更します$subject
。output-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ěte
(Hello, 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()
たように、を使用することもできますが、誤って使用するのは簡単です。
mb_internal_encoding()
mbstring関数の内部で使用されるエンコーディングを設定するためにを使用する必要があります。mb_*
関数は、入力文字列がこのエンコーディングであると想定しています。注意:の2番目のパラメーターはmb_encode_mimeheader()
、入力文字列とは何の関係もありません(マニュアルの内容にかかわらず)。<charset>
これは、エンコードされた単語のに対応します(上記の「どのように機能しますか?」を参照)。入力文字列は、BまたはQエンコーディングに渡される前に、内部エンコーディングからこのエンコーディングに再コード化されます。
PHP 5.6以降、内部エンコーディングの設定は必要ない場合があります。これは、基になるmbstring.internal_encoding
構成オプションが廃止されdefault_charset
、デフォルトでUTF-8に設定されているオプションが採用されたためです。これは単なるデフォルトであり、コードのデフォルトに依存することは不適切である可能性があることに注意してください。
入力文字列にはヘッダー名とコロンを含める必要があります。RFCは行の長さに強い制限を課しており、最初の行にも適用する必要があります。$indent
別の方法は、5番目のパラメーター( ; 2015年9月現在の最後のパラメーター)をいじることですが、これはさらに便利ではありません。
実装にはバグがある可能性があります。正しく使用しても、出力が壊れることがあります。少なくともこれは、マニュアルページの多くのコメントが言っていることです。私は何とか問題を見つけることができませんでしたが、エンコードされた単語の実装には注意が必要です。またはに潜在的または実際のバグを見つけた場合は、コメントでお知らせください。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テキストのエンコードは、関連していますが異なる質問です。このトピックについて詳しく知りたい場合は、検索してください。答えが見つからない場合は、別の質問をして、コメントでそれを指摘してください。