mb_encode_mimeheaderにも改行コードを指定mb_encode_mimeheader関数には文字コードがUTF-8で、エンコードはbase64だよ、という指定のほかに改行コードの指定もできます。$crlf);サンプルではわざと長い件名にして、絵文字まで入れていますが、これがエンコードされると下のような文字列になります。=?UTF-8?B?44Gf44KB44KP44GW44Go6ZW344GP44GX44Gm44G/44Gf8J+Yig==?=PHPのマニュアルによると74文字ごとに改行を入れる仕様のようです。RFC2045の仕様、76文字から記号分の余裕を持たせているのかな?Gmail等で受信したときに件名が文字化けする場合は、メニューからメッセージのダウンロードを選べば拡張子.emlというファイル(中身はテキストファイル)が取得できるので、テキストエディターで中身を確認してみると良いでしょう。長い件名なのに78文字でCRLFが入っていなかったり、CRLFがLFだけになってしまっていたり、文字化けの原因を特定することができます。ちなみにCRLFの代わりにLFでも正常に表示されるメールクライアントも多数ありますし、事実Gmailでは問題なく表示できちゃったりしますが、RFC違反なのでちゃんと行末がCRLFになるようにして送信しましょう。BOUNDARY(=境界線)は前後に付けるHTMLメールはマルチパートメールとして送信するのが一般的です。マルチパートメールとは、異なる形式のメッセージを複数含んだメールのことですね。テキストメール+添付メール(バイナリ)だったり、今回の例だとテキストメール+HTMLメールです。HTMLメールが受信できないメールクライアントなんてないやろ、と思われるかもですが、古いメールクライアントはもちろんのこと、一部のレンタルサーバーに標準付属のWebメールシステム(具体例を出すとさくらインターネットとか)ではHTMLメールに対応していないケースもあるので、一応マルチパート形式にしておくほうが無難。HTMLメールの表示はできずとも、テキストメール部分を表示しつつ、添付ファイルとしてHTMLファイルを開けるようにしているメールクライアントなんてのもありますから。BOUNDARYはその名のとおり、境界線の意味で、ここからがテキストメールだよー、そしてここからがHTMLメールだよー、というような区切りに使います。とでも指定しておけば良いし、そんなメルマガもちょくちょく見かけますが、まぁせっかくなので真面目に乱数を指定しています。"__";こんなふうにuniqidとrand関数を使ってランダムな文字列を設定しておけば、メール本文とかぶっておかしくなることもないだろうってことですね。このメールはマルチパートですよー、ということと、boundary=で、境界線の記号はコレだよー、と先ほどのランダム文字列を指定しています。続いて、メッセージ部分は下記のとおり、ハイフン2つから開始する境界線で分け、それぞれのContent-TypeやContent-Transfer-Encodingを指定する仕組み。base64<<HTMLメールの本文>>--__BOUNDARY__205931421862c3f3f5c8c8a2.88945220__ここでも大事なのが改行です。CRLFで送信するのはもちろんのこと、Content-Type/Content-Transfer-Encoding等の指定の後、本文が始まる前に空行が必ず必要です。base64と書いた直後にすぐ本文を書いちゃダメってこと。これ、案外ハマります。いうのも、Gmail等の一部のメールクライアントではこのくらいのエラーは吸収して正常に表示してくれちゃうんですよね。ところが、Yahooメールでは文字化けどころか、本文が一切表示されない状態になります。ややこしいことに同じGmailでも、POP受信で外部メールを取り込むように設定したGmailだと、この書き方で文字化けが発生。わけわかんないでしょ?ともあれ、仕様を守っていないほうが悪いので、ちゃんと本文の前に空行を入れるようにしましょう。本文中の1行の文字数制限にはchunk_splitを使う↓この部分。$crlf);base64_encodeでエンコードした後、chunk_splitで指定文字数(ここでは76文字)ごとにCRLFで改行を入れています。これは最初に書いた「1行の文字数は78文字を超えないことが望ましい」というRFCの仕様を守るためですね。.....................78じゃなくて76なのは何でなんだ。chunk_split関数のデフォルト値が76だからそう書いたのですが、chunk_splitはRFC2045に準拠しているとのこと。たしかに76文字とある...。RFC5322では1行あたりの文字数が78文字を超えないことが望ましい、RFC2045ではエンコードされた文字が76文字を超えてはいけない。頭がこんがらがりそうだけど、ともかく、そういう仕様なのだそうです。オプションを指定することで、メールヘッダーのReturn-Pathが設定できます。ここに差出人と同じメールアドレスが入っていないと迷惑メール判定されるケースがあったので、できれば設定しておいたほうが良いでしょう。ちなみに大量に発信するメルマガ発行システムなどではあえてReturn-Pathに別のメールアドレスを指定しておき、定期的にそのメアドをチェックして不達エラーが届いていないか確認して、一定回数以上のエラーがあったらメルマガから登録解除する、なんて仕組みを採用しているところもあります。人間がメールクライアントで[返信]ボタンを押したときの送り先(返信先)はFromで指定されたメアドになりますが、postmasterなどのシステムが不達メールなどの通知を行う先はReturn-Pathに指定されたメアドになるんです。これを利用してアドレス収集を行うスパム業者がいるため、FromとReturn-Pathが同じドメインでない場合には迷惑メールの可能性が高い、と判断されるのかも知れません。まとめHTMLメールを送信するサンプルコードだけ載せて終わりにするつもりが、これだと1週間後の自分は内容忘れてそうだなぁ~と感じたので、補足説明を付けているうちにえらい長くなってしまった...。改行コードが鬼門です。文字コードはUTF-8なのはもうデファクトスタンダードだし、するとエンコードは自然とbase64が効率的になります。あとは改行コードの問題のみ。qmailが勝手にLFをCRLFに勝手に変換してしまうのは有名な話なのですぐに気が付きましたが、マルチパートメールにした際、本文手前に空行が必要という点を見逃していて丸1日悩みました。したら忘れて同じことを繰り返しそうな気がするので、願わくば将来の自分がこの記事にたどり着きますように。関連記事PHPでCookieを保持しつつWebスクレイピングする例PHPを使ったWebスクレイピングといえば5年ほど前に投稿した「PHPでHTMLを簡単に解析できるDOMDocument」でほとんどコト足りていましたし、これでは取得できないページでも「VBScrip02023-10-02INSERT文を一括処理して5万件のデータ追加を12倍高速化するもう半年以上前になりますが、「PHPでSQLiteからMySQLへテーブル構造を変換しデータも移行する例」と題して、SQLiteからMySQLへデータベースを移行するPHPスクリプトを公開/解説しまし02022-10-25PHPのjson_encodeで空白が返ってきてしまう場合のエラー確認方法PHPでJSON形式のデータを作る際によく使うjson_encode関数ではエラー(=変換できない事象)が起こっても空文字が返ってくるだけなので、JSON_THROW_ON_ERRORオプションを付け02022-10-14PHPでファイル一覧を取得するならglobよりopendirのほうが27倍早いPHPのglob関数って便利ですよね。IPが枯渇する、する、というわりに日本では実はけっこう余っているとか、ついこの前、アメリカでも突如1億7500万個ものIPアドレスが放出されたとか聞くし、そ02022-04-21Web版Gmailは絵文字を画像として扱うためCSSの書き方には注意しようPHPでHTMLメールを送信するプログラムを書いていて、なぜか絵文字だけが予期しない形や位置に移動してしまうトラブルにハマったので備忘録として残しておきます。03:49大変詳しい説明ありがとうございました。サンプルコードを試したところ、本文が空の状態で届いてしまいます。iPhoneとMacの標準メーラーで、そのような状況です。ちなみに件名は文字化けせず、絵文字もちゃんと表示されております。'--'.$boundary.$crlf.$crlf;自分では確認できませんが、これと同様のコードでPHPからHTMLメールを送信し、知り合いのiPhoneユーザーからは正常に表示できていると聞いております。05:50返信ありがとうございます。空行を追加してみましたが、やはり「このメールには本文がありません」と出てしまいました。PHPを始めとする環境のせいでしょうか。実は現在使用しているHTMLメールを送信するスクリプトでたまに文字化けが発生し、原因を探っているところでこちらのページに辿り着いた次第です。ルールがあることを知れただけでも大収穫でした。ありがとうございました。14:39iPhoneやMacで出来るのかわかりませんが、受信側でメールのソース(生データ)表示をして、ヘッダー、改行コード、1行あたりの文字数、それぞれが適切かお調べになるのが良いかと思います。自分の場合はAndroidのGmailではソース表示ができなかったので、Web版のGmail/Liveメール/Yahooメール等々、複数のWebメールで受信しソースを確認しました。それで思い出しましたが、この記事を書いた当時はYahooメールだけ本文が空白になる現象に悩まされていました。qmailが改行コードを勝手にCRCRLFへ変換してしまい、ヘッダーの終わりがわからなくなっていたのが原因だったような...。半年以上前なので記憶があいまいですが、そういえば件名と本文はLFで、ヘッダーはCRLFという変則的な対応をした気も...。当時書いていたコードを引っ張り出してきました。改行コードの処理がサンプルとちょっと違いますね...。'-f'.$mail_return);このソースを見る限り、qmailは件名と本文中のLFをCRLFへ強制変換するのでそれに対応し、ヘッダー部分だけは普通にCRLFを出力しているようです。記事のサンプルコードでも複数のMTA&複数のWebメールで動作確認した上で投稿した気がしますが、半年も経つと自信がなくなってきます。・件名を20文字以下の英数字のみにして本文が表示されないか試してみる・Yahooメールで受信するとどうなるか確認してみる(ソースも表示可能)このあたりを試されると良いかも知れません。16:06こんにちは。ほんともう度々ありがとうございます!改行コードを試してみましたが、やはりダメでした。'--'.$boundary.'--'.$crlf;私が思うに、2番目の'--'が本文の終わりを示し、それがなかったからメーラーが本文を取得できずにいたのではないかと。賢いメーラーは気を利かせてくれるのだと思いますが、少なくともMacとiPhoneの標準メーラーでは必須(バージョンにもよる?)・・・ということでしょうか。今回は大変勉強になりました。16:22こちらこそ結果をお知らせ頂けて勉強になりました!そういえば、たしかにこの部分を付けるか付けないかで挙動が変わるWebメールサービスもあったことを思い出しました。'--'.$boundary."--".$crlf.$crlf;記事中のサンプルも修正しておくことにします。コメント投稿直後ならコメントの編集/削除が可能です。※24時間経過するか、新しく投稿した場合は古い投稿の編集や削除は出来ません。※ブラウザでCookieが受け入れられない場合はこの機能は使えません。非公開にチェックを入れたコメントは管理人のみ閲覧できます。コメント投稿のガイドライン誰もが閲覧できるコメント欄ですので個人が特定できる情報等は書き込まないようにしましょう。他人のHPやブログの文章を掲載する場合は著作権法第32条「引用」の条件を満たすかご確認ください。誹謗中傷にあたる表現などは場合によっては法的な責任を問われる可能性があるためご注意ください。とっちらか~るTOP。