ど素人から毛を生やす。<延>

[PHP]受信したメールの件名が文字化けしている場合。

Web > PHP 2021年2月4日(最終更新:3月前)

どもです。
PHPにて、自身のサーバからメールを取り込んでなんやかんやする旧いシステムの改修を行っていました。

すると、届いたメールの本文が文字化けしておりました。

ど4Rotoから毛を生やす(*´▽`*)

=E3=81=A94Roto=E3=81=8B=E3=82=89=E6=AF=9B=E3=82=92=E7=94=9F=E3=82=84=E3=81=99= (*=C2=B4=E2=96=BD=EF=BD=80*)

これは、メールでは文字列をASCII文字(7bit文字)に変換(エンコード)しており、この再変換(デコード)を行わなければならないからです。
この場合はエンコード形式がquoted-printableなので、imap_qprint()で解決できます。

var_dump(imap_qprint('=E3=81=A94Roto=E3=81=8B=E3=82=89=E6=AF=9B=E3=82=92=E7=94=9F=E3=82=84=E3=81=99=
(*=C2=B4=E2=96=BD=EF=BD=80*)'));
	// -> string(40) "ど4Rotoから毛を生やす(*´▽`*)"

しかし、メールの件名がエンコードされていた場合。
この場合はimap_qprint()では文字化けが直せないのです。

というのも、例えば文字セットがUTF-8、エンコードがquoted-printableの場合。

ど4Rotoから毛を生やす(*´▽`*)

=?utf-8?Q?=E3=81=A94Roto=E3=81=8B=E3=82=89=E6=AF=9B=E3=82=92=E7=94=9F=E3=82=84=E3=81=99=?=
=?utf-8?Q?(*=C2=B4=E2=96=BD=EF=BD=80*)?=

メールヘッダ情報に限っては、こうなってしまうんですね。なぜだ。
ほかの文字セットとエンコードの組み合わせについては、こちらのQiitaで網羅されているので参照していただきたい。

Q.で、Linuxコマンドだと一撃対応できるとのことですが、PHPでは一撃対応できんの??
A.どうやら、PHPでは一撃では対応できないようです。

$subject = "=?utf-8?Q?=E3=81=A94Roto=E3=81=8B=E3=82=89=E6=AF=9B=E3=82=92=E7=94=9F=E3=82=84=E3=81=99=?=
 =?utf-8?Q?(*=C2=B4=E2=96=BD=EF=BD=80*)?=";

$array = imap_mime_header_decode($subject);

print_r($array);
  //Array
  //(
  //    [0] => stdClass Object
  //        (
  //            [charset] => utf-8
  //            [text] => ど4Rotoから毛を生やす
  //        )
  //
  //    [1] => stdClass Object
  //        (
  //            [charset] => utf-8
  //            [text] => (*´▽`*)
  //        )
  //
  //)

$decode_subject = '';
foreach($array as $one_line){
    $decode_subject .= $one_line->text;
}

var_dump($decode_subject);
  //string(41) "ど4Rotoから毛を生やす(*´▽`*)"

imap_qprint()ではなく、imap_mime_header_decode()を使用する必要があるようです。
「非 ASCII テキストの MIME メッセージヘッダエクステンションをデコードします」とか言われてもわけわからんちんの日本語でおkですが、要は、文字列がこの状態だった場合、一行ごとに文字セット(charset)と8bit文字にデコードされた文字列(text)の配列に変換されるという認識で良いです。
ちなみに通常のquoted-printableをデコードする際はimap_qprint()、Base64はbase64_decode()と、異なる関数を使用する必要がありますが、imap_mime_header_decode()はエンコード形式に依存せずに使えます。

上記の場合、メールの文字セットがISO-2022-JPだったり、こっちの文字コードがUTF-8以外だったりすると、無事に8bitにデコードしてもまだ文字化けしているため、実用するならmb_convert_encoding()もかけてやる必要があります。

$after_charset = 'EUC-JP';

$subject = "=?utf-8?Q?=E3=81=A94Roto=E3=81=8B=E3=82=89=E6=AF=9B=E3=82=92=E7=94=9F=E3=82=84=E3=81=99=?=
 =?utf-8?Q?(*=C2=B4=E2=96=BD=EF=BD=80*)?=";

$array = imap_mime_header_decode($subject);

$decode_subject = '';
foreach($array as $one_line){
  if($one_line->charset == 'default'){
    $decode_subject .= $one_line->text;
  }else{
    $decode_subject .= mb_convert_encoding($one_line->text, $after_charset, $one_line->charset);
  }
}

var_dump($decode_subject);
  //string(41) "ど4Rotoから毛を生やす(*´▽`*)" ※EUC-JP

charset == 'default' というのは、imap_mime_header_decode()の解説の方にありますが、7bit状態の文字列からUTF-8とかiso-2022-jpとかが読み取れない場合に得られる値です。

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)