問題になった現象
PHPMailer を使っているシステムで、下記のような現象が報告されてきたので、対処方法について書いておきます。
- 髙(はしごだか)、﨑(たちざき)といった文字が、必ず文字化けする。
- 日本語のファイル名を添付すると、ファイル名が文字化けすることがある。特定の文字が必ず化けるのではなく、特定の文字順のときにだけ化ける。
対策1. キャラセットを UTF-8 に変更
以前はガラケーを考慮して、キャラセットを iso-2022-jp(JIS)で送信していたので、髙(はしごだか)、﨑(たちざき)といった範囲外の文字は化けてしまいます。今回はガラケーを考慮せずとも良い案件であったため、キャラセットを UTF-8 にすることで文字化けしないようにします。
対策2. Localeを明示的に指定
添付ファイル名の文字化けの方の原因は、ファイル名を base64 で変換した際に、"/" が出現する場合に文字化けしていたことがわかりました。PHPMailer#addStringAttach()するときに、内部的に basename() が行われるのですが、Localeを指定していないとうまくファイル名が解析できないようです。
修正前のソース
本当はもっと複雑なソースコードなのですが、ざっくりと下記のように実装されていました。
var $mailer = new PHPMailer(TRUE); // 文字セットとエンコーディングの設定 $mailer->CharSet = 'iso-2022-jp'; $mailer->Encoding = '7bit'; // 宛先 山田 太郎 <yamada@example.com> $address = $this->input->post('f_address'); // yamada@example.com $address_name = $this->input->post('f_address_name'); // 山田 太郎 $enc_address_name = mb_encode_mimeheader($address_name, 'ISO-2022-JP', 'B', "\n"); $mailer->_mailer->addAddress($address, $enc_address_name); // メール表題 $subject = $this->input->post('f_subject'); $mailer->Subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B', "\n"); // メール本文(WYSWYGエディタで入力したもの) $body = $this->input->post('f_body'); // ・UTF-8 ⇒ ISO-2022-JP へ変換 $mailer->Body = mb_convert_encoding($body, 'ISO-2022-JP', 'UTF-8'); $mailer->AltBody = mb_convert_encoding(strip_tags($body), 'ISO-2022-JP', 'UTF-8'); // 添付ファイル名(画面からアップロードされる) $file_name = $_FILES['f_file']['name'][0]; $file_type = $_FILES['f_file']['type'][0]; $file_tmp_name = $_FILES['f_file']['tmp_name'][0]; // ・UTF-8 ⇒ JIS へ変換 $enc_file_name = mb_convert_encoding($file_name, 'JIS', 'UTF-8'); $mime_file_name = mb_encode_mimeheader($enc_file_name, 'JIS'); $mailer->addAttachment($file_tmp_name, $mime_file_name, 'base64', $file_type); // 添付ファイル(データベース等から取得したものを添付するパターン) $file_name_from_db = $rs['file_name']; $file_content_from_db = $rs['file_content']; // ・UTF-8 ⇒ JIS へ変換 $enc_file_name_from_db = mb_convert_encoding($file_name_from_db, 'JIS', 'UTF-8'); $mime_file_name_from_db = mb_encode_mimeheader($enc_file_name_from_db, 'JIS'); $mailer->addStringAttachment($file_content_from_db, $mime_file_name_from_db); $mailer->send();
修正後のソース
ポイントは下記の通り。
- CharSetを UTF-8 に設定
- Endcoding を base64 に設定
- CharSetを UTF-8 に設定したので、mb_convert_encoding は一切不要
- CharSetを UTF-8 に設定したので、mb_encode_mimeheader では UTF-8 を指定
- CharSetを UTF-8 に設定したので、Locale は ja_JP.UTF-8 を指定
- PHPMailerのaddAttach()の第2引数で渡すファイル名は、PHPMailer自身が適切に mimeheader をつけてくれるので、そのままのファイル名を渡す
var $mailer = new PHPMailer(TRUE); $this->_mailer->CharSet = 'UTF-8'; $this->_mailer->Encoding = 'base64'; // 宛先 山田 太郎 <yamada@example.com> $address = $this->input->post('f_address'); // yamada@example.com $address_name = $this->input->post('f_address_name'); // 山田 太郎 $enc_address_name = mb_encode_mimeheader($address_name, 'UTF-8', 'B', "\n"); $mailer->_mailer->addAddress($address, $enc_address_name); // メール表題 $subject = $this->input->post('f_subject'); $mailer->Subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\n"); // メール本文(WYSWYGエディタで入力したもの) $body = $this->input->post('f_body'); // もともと UTF-8 なのでそのままセット $mailer->Body = $body; $mailer->AltBody = strip_tags($body); // 添付ファイル(画面からアップロードされたものを添付するパターン) $file_name = $_FILES['f_file']['name'][0]; $file_type = $_FILES['f_file']['type'][0]; $file_tmp_name = $_FILES['f_file']['tmp_name'][0]; // ・UTF-8 ⇒ JIS へ変換 $mime_file_name = mb_encode_mimeheader($file_name,, 'UTF-8', 'B', "\n"); $mailer->addAttachment($file_tmp_name, $mime_file_name, 'base64', $file_type); // 添付ファイル(データベース等から取得したものを添付するパターン) $file_name_from_db = $rs['file_name']; $file_content_from_db = $rs['file_content']; // PHPMailer#addStringAttach() で basename() を使用しているため、 // base64変換時に / が出現するとファイル名をうまく判定できないことがある。 // そのため、事前に Locale を設定しておく必要がある。 setlocale(LC_ALL, 'ja_JP.UTF-8'); $mailer->addStringAttachment($file_content_from_db, $file_name_from_db); $mailer->send();