PHP mail()関数で日本語を文字化けさせずに送信する
PHPからメール送信することは可能です。
メールフォームなどに入力された内容をメールで送信することはよくあることです。
PHPには「mail関数」と呼ばれる、メールを送信する仕組みが標準で備わっています。
これを使えば割と簡単にメール送信できます。
ただ、よく文字化けします。
とくに日本語はよく化けます。
何とかならないものか四苦八苦しながらも対処いたしましたので、忘備録として記録いたします。
mail()関数とはこのような関数です
基礎情報として、mail()関数について記述します。
といっても以下のように記述するだけです。
mail( [送信先メールアドレス], [メールタイトル], [本文], [送信元メールアドレス]);
例えば、以下のように記述します。
mail(“sousin@office-obata.com”, “メールタイトル文”, ”本文 テキストテキスト・・・”, “moto@officeobata”);
※メールアドレスは実在しないものです。
なんだ簡単じゃないかと思い、さっそく実験してみました。
実験①:特に何も考えずmail送信してみました。
PHPでメール送信するだけならば、フォームを使わなくてもできます。
テスト用に以下のプログラムを作成しテストしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "From: テスト送信者<moto@office-obata.com>"; // メールタイトル $subject = "メール送信テスト①"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // メール送信 mail($to, $subject, $body, $from); ?> |
実際に受信してみた結果がこちらです。
OUTLOOKで受信したら特に問題はなかったのですが、Thunderbirdでメール受信してみたところ、「差出人」と「件名」が文字化けしました。
なんとなく理由は想像がつきます。
OUTLOOKは文字コードが「Shift-JIS」ベースですが、Thunderbirdは「UTF-8」
テスト用のPHPは日本語の文字コードを「Shift-JIS」で組んでおります。
Thunderbirdは日本語部分のデフォルトが「UTF-8」です。
なのでOUTLOOKでは文字化けせず、Thunderbirdは文字化けしたのだと思われます。
ちなみに、
メールの受信確認をする際は、OUTLOOKとThunderbirdの2つを使うと、問題点の検出がやりやすいです。
OUTLOOKだけでチェックすると、特定メーラー文字化けを見逃しがちです。
実験②:差出人、件名をUTF-8にしてみました
差出人、件名が文字化けしたので、ならばその2つを「UTF-8」に変換してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "From: テスト送信者<moto@office-obata.com>"; // メールタイトル $subject = "メール送信テスト②"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // 文字コードをUTF-8に変換 $from = mb_convert_encoding($from, 'UTF-8', 'SJIS-win'); $subject = mb_convert_encoding($subject, 'UTF-8', 'SJIS-win'); // メール送信 mail($to, $subject, $body, $from); ?> |
その結果がこちらです。
なるほど。一方を直せば反対側がだめになるパターンです。
この場合、どうすればよいのでしょうか?
実験③:メールヘッダーを付け加えてみた。
そもそもベースの文字コードが違うメーラーの場合、Shift-JIS、UTF-8のどちらを使用しても、かならずどちらかのメーラーは文字化けします。
このような場合、どうすればよいのでしょうか?
ググって調べて見たところ、以下のような対策が必要だということがわかりました。
- メールヘッダーに文字コードを記述するとメーラーは、メールヘッダーに記述された文字コードで表示される。
なるほど、表示する文字コードを事前に指定すれば良い。
ということがわかりました。
しかし、ここで疑問がわきました。
メールヘッダーってなに?PHPのmail関数でどうやって指定するの?
ちなみに、以前こんな記事も書いていました。
そういえば、PHPから送信するときにメールヘッダーを指定していないと思いました。
しかし、どうやって指定すればよいかわからなかったため、ググってさらに調べました。
その結果対応してみたものが、以下のコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "From: テスト送信者<moto@office-obata.com>"; // メールタイトル $subject = "メール送信テスト③"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // 文字コードをUTF-8に変換 $from = mb_convert_encoding($from, 'UTF-8', 'SJIS-win'); $subject = mb_convert_encoding($subject, 'UTF-8', 'SJIS-win'); // PHPの文字コードをUTF-8に指定 mb_internal_encoding("utf-8"); //headerを設定 $header = array(); $header['MIME-Version'] = "1.0"; $header['Content-Type'] = "text/plain; charset=UTF-8"; $header['Content-Transfer-Encoding'] = "8bit"; $header['From'] = $from; //headerを編集 $set_header = array(); foreach ($header as $key => $val) { $set_header[] = $key . ': ' . $val; } // ヘッダーを1文に結合 $send_header = implode("\n", $set_header); // メール送信 mail($to, $subject, $body, $send_header); ?> |
急に複雑になりました。
要は、いままで「送信元情報」を設定していた部分にメールヘッダー情報を記述します。
メールヘッダーはむつかしくてよくわからないので、固定記述でコピペです。
その結果がこちらです。
さらにひどくなりました・・・・。
今度は本文も文字化けしました。
ただ、件名はどちらも正しく表示されています。
ひどいなりにも、一歩前進です。
まずは本文の文字化けを何とかします。
実験④:本文の文字化けを直す
メールヘッダーを記述したらなぜ本文が文字化けしたのか?
ちょっと焦る感じの現象ですが、冷静になって考えたら理由がわかります。
- メールヘッダーには記載文字コードはUTF-8と記述した
- 本文はもともと文字化けしていなかったのでShift-JISのまま
なんか理由が見えてきました。
これは本文を「UTF-8」に変換してやればよさそうです。
それがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "From: テスト送信者<moto@office-obata.com>"; // メールタイトル $subject = "メール送信テスト④"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // 文字コードをUTF-8に変換 $from = mb_convert_encoding($from, 'UTF-8', 'SJIS-win'); $subject = mb_convert_encoding($subject, 'UTF-8', 'SJIS-win'); $body = mb_convert_encoding($body, 'UTF-8', 'SJIS-win'); // PHPの文字コードをUTF-8に指定 mb_internal_encoding("utf-8"); //headerを設定 $header = array(); $header['MIME-Version'] = "1.0"; $header['Content-Type'] = "text/plain; charset=UTF-8"; $header['Content-Transfer-Encoding'] = "8bit"; $header['From'] = $from; //headerを編集 $set_header = array(); foreach ($header as $key => $val) { $set_header[] = $key . ': ' . $val; } // ヘッダーを1文に結合 $send_header = implode("\n", $set_header); // メール送信 mail($to, $subject, $body, $send_header); ?> |
実験③との違いは、本文をUTF-8に変換を追加したのみです。
結果はこちら。
本文の文字化けが直りました。
文字コード変換は当たりでした。
残るは、差出人部分を何とかすればOKです。
しかしメールヘッダーをつけているのに、差出人だけなぜ文字化けするのでしょうか?
そして化けているのはOUTLOOKだけです。
考えてもよくわからなかったので、さらにググって調べました。
そしたら、次のようなことがわかりました。
- 差出人はメールヘッダー内に記述されるため、MIME変換しなければならない。
なるほど、差出人名はメールヘッダー内だから、文字コード指定していても文字化けしていたようです。
でも疑問がわきました。
MIME変換って何?
実験⑤:差出人名をMIME変換をしてみる(注:失敗例)
PHPでMIME変換をググってみたら、あっさり疑問は解決しました。
mb_encode_mimeheader()というMIME変換してくれる関数がPHPにはありました。
差出人をこの関数を通せばMIME変換してくれます。
早速差出人をMIME変換する記述に書き換えてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "テスト送信者<moto@office-obata.com>"; // メールタイトル $subject = "メール送信テスト⑥"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // 文字コードをUTF-8に変換 //$from = mb_convert_encoding($from, 'UTF-8', 'SJIS-win'); $from = mb_encode_mimeheader($from); $subject = mb_convert_encoding($subject, 'UTF-8', 'SJIS-win'); $body = mb_convert_encoding($body, 'UTF-8', 'SJIS-win'); // PHPの文字コードをUTF-8に指定 mb_internal_encoding("utf-8"); //headerを設定 $header = array(); $header['MIME-Version'] = "1.0"; $header['Content-Type'] = "text/plain; charset=UTF-8"; $header['Content-Transfer-Encoding'] = "8bit"; $header['From'] = $from; //headerを編集 $set_header = array(); foreach ($header as $key => $val) { $set_header[] = $key . ': ' . $val; } // ヘッダーを1文に結合 $send_header = implode("\n", $set_header); // メール送信 mail($to, $subject, $body, $send_header); ?> |
差出人部分のUTF-8変換をやめ、MIME変換してみました。
その結果がこちらです。
Thunderbird側は問題ありませんでしたが、OUTLOOK側が変な文字列になりました。
何がいけないのでしょうか?
PHPの記述を見直していたら、気づきました。
送信元メールアドレスごとMIME変換していました。
送信元情報は、
1 2 3 4 |
$from = "テスト送信者<moto@office-obata.com>"; |
というように、日本語名とメールアドレスをまとめて気記述していました。
でもMIME変換は日本語部分だけでよいのではないかと思いました。
そこで送信元名と送信元メールアドレスを分離して記述することにしました。
実験⑥:差出人名をMIME変換をしてみる(成功例)
送信元情報を分割し、日本語名だけをMIME変換するように直しました。
それがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?php // 宛先 $to = "sousin@office-obata.com"; // 送信元 $from = "moto@office-obata.com"; // 送信者名 $from_name = "テスト送信者"; // メールタイトル $subject = "メール送信テスト⑦"; // メール本文 $body = <<< EOM メール本文 ■特殊文字 ①②③ ㈱㌔髙 テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト テキストテキストテキストテキストテキストテキストテキスト EOM; // 文字コードをUTF-8に変換 //$from_name = mb_convert_encoding($from_name, 'UTF-8', 'SJIS-win'); $from_name = mb_encode_mimeheader($from_name); $subject = mb_convert_encoding($subject, 'UTF-8', 'SJIS-win'); $body = mb_convert_encoding($body, 'UTF-8', 'SJIS-win'); // PHPの文字コードをUTF-8に指定 mb_internal_encoding("utf-8"); //headerを設定 $header = array(); $header['MIME-Version'] = "1.0"; $header['Content-Type'] = "text/plain; charset=UTF-8"; $header['Content-Transfer-Encoding'] = "8bit"; $header['From'] = "${from_name}<${from}>"; //headerを編集 $set_header = array(); foreach ($header as $key => $val) { $set_header[] = $key . ': ' . $val; } // ヘッダーを1文に結合 $send_header = implode("\n", $set_header); // メール送信 mail($to, $subject, $body, $send_header); ?> |
日本語部分だけをMIME変換し後から結合します。
その結果がこちらです。
OUTLOOKもThunderbirdも文字化けしなくなりました。
かなりしんどかったですが、無事解決することができました。
メールを文字化けさせないポイント
- メールヘッダーを正しく記述すること。
- そして日本語部分を正しい文字コードに変換すること。
- 差出人日本語名はMIME変換すること。
この3つが大事であることをよくよく学びました。
mail送信関数にはmb_send_mail()というものもあります。
ただ、うまく送信できない。文字コード変換がかえって大変などの理由からmail関数のみでやってみました。
これでmail関数だけでそれなりにメール送信ができることがわかりました。
また一つ良い経験をいたしました。