質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

mb_convert_encoding で文字エンコーディングを変換したときに、メーラー側ではその一部分を指定された文字エンコーディングで表示するのですか?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 5,693

7968

score 249

現在、メールフォームの文字コードについて勉強しております。

入力、確認、送信と画面を遷移するメールフォームを作成しました。

送信画面のコードを末尾に記載致します。

mb_language('ja') を指定しております。

ja または Japanese  の場合は、ISO-2022-JP/Base64 でエンコードされます。

ISO-2022-JP は、丸囲み文字の ①②㈱髙 や アイウエオ などの半角カナには対応しておりません。

そのため、mb_convert_encoding を使って ISO-2022-JP-MS に文字エンコーディングを変換しています。

作成したメールフォームから ①②㈱髙アイウエオ を入力して送信したら、問題なく表示されました。

Windows7 の環境で WindowsLive Mail 2012 と Thunderbird 45.8.0 のメーラーで問題なく表示されました。

受信したメールのソースを確認しましたが、Content-Type: text/plain; charset=ISO-2022-JP としか記載なく ISO-2022-JP-MS の表記がありません。

なぜ、メール本文の ①②㈱髙アイウエオ は正しく表示されるのでしょうか?

ご存じの方いれば、教えてください((_ _ (´ω` )ペコ

入力画面

<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>入力画面</title>
</head>
<body>

<h1>お問い合わせ(入力画面)</h1>

<form method="post" action="confirm.php">
<table>
    <tr>
        <th>お名前(必須)</th>
        <td><input type="text" name="name"></td>
    </tr>
    <tr>
        <th>ふりがな(必須)</th>
        <td><input type="text" name="ruby"></td>
    </tr>
    <tr>
        <th>メールアドレス(必須)</th>
        <td><input type="text" name="mail"></td>
    </tr>
    <tr>
        <th>内容(必須)</th>
        <td><textarea name="content"></textarea></td>
    </tr>
</table>
<button>送信内容確認</button>
</form>

</body>
</html>

確認画面

<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');

// IEで発生するコンテンツタイプSniffing対策
header('X-Content-Type-Options: nosniff');

// XSSフィルタの設定
header('X-XSS-Protection:1; mode=block');

// HttpOnly属性 の指定
ini_set('session.cookie_httponly', 1);

// セッション開始
session_start();

// HTML特殊文字をエスケープする関数
function h($str) {
    return htmlspecialchars($str,ENT_QUOTES,'UTF-8');
}

/* --------------------------------------------------
    トークンの作成(CSRF対策)

    ※使用しているPHPのバージョン・環境にあわせて
     トークンを選んでね。不要なトークは削除してね。
-------------------------------------------------- */

// PHP 7.0 以降
//if(!isset($_SESSION['token'])) {
//    $_SESSION['token'] = bin2hex(random_bytes(32));
//}

// PHP 5.3 ~ 5.x ※OPENSSL導入済
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
}

// PHP 5.3 未満
//if(!isset($_SESSION['token'])) {
//    $_SESSION['token'] = hash('sha256', session_id());
//}

// トークンを代入
$token = $_SESSION['token'];

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>確認画面</title>
</head>
<body>

<h1>お問い合わせ(確認画面)</h1>

<?php

// POSTされたデータを変数に代入(magic_quotes_gpc = On + NULLバイト 対策)
foreach (array('name','ruby','mail','content') as $v) {
    $$v = filter_input(INPUT_POST, $v, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW);
}

$error_flag = 0;

// 必須項目は未入力をチェック
if ($name === '' || $name === NULL) {
    echo '<p>お名前をご入力してください。</p>';
    $error_flag = 1;
} elseif (mb_strlen($name) > 50) {
    echo '<p>お名前は 50 文字以内で入力してください。</p>';
    $error_flag = 1;
}

if ($ruby === '' || $ruby === NULL) {
    echo '<p>ふりがなをご入力してください。</p>';
    $error_flag = 1;
} elseif (mb_strlen($ruby) > 50) {
    echo '<p>ふりがなは 50 文字以内で入力してください。</p>';
    $error_flag = 1;
}

if ($mail === '' || $mail === NULL) {
    echo '<p>メールアドレスをご入力してください。</p>';
    $error_flag = 1;
} elseif (mb_strlen($mail) > 100) {
    echo '<p>メールアドレスは 100 文字以内で入力してください。</p>';
    $error_flag = 1;
} elseif (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
    echo '<p>メールアドレスの形式が正しくありません。</p>';
    $error_flag = 1;
}

if ($content === '' || $content === NULL) {
    echo '<p>内容をご入力してください。</p>';
    $error_flag = 1;
} elseif (mb_strlen($content) > 500) {
    echo '<p>内容は 500 文字以内で入力してください。</p>';
    $error_flag = 1;
}

// エラーがある場合は、戻るボタンを表示し、エラーがない場合は、確認画面を表示
if ($error_flag === 1) {
    echo '<button onClick="history.back(); return false;">戻る</button>';
} else {
    // セッション変数に代入
    $_SESSION['name'] = $name;
    $_SESSION['ruby'] = $ruby;
    $_SESSION['mail'] = $mail;
    $_SESSION['content'] = $content;

    // 確認用画面の表示
    ?>

    <form method="post" action="send.php">
        <table>
            <tr>
                <th>お名前</th>
                <td><?php echo h($name); ?></td>
            </tr>
            <tr>
                <th>ふりがな</th>
                <td><?php echo h($ruby); ?></td>
            </tr>
            <tr>
                <th>メールアドレス</th>
                <td><?php echo h($mail); ?></td>
            </tr>
            <tr>
                <th>内容</th>
                <td><?php echo nl2br(h($content)); ?></td>
            </tr>
        </table>
        <input type="hidden" name="token" value="<?php echo h($token); ?>">
        <button>送信</button>
    </form>

    <?php
}

?>


</body>
</html>

送信画面

<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');

// IEで発生するコンテンツタイプSniffing対策
header('X-Content-Type-Options: nosniff');

// XSSフィルタの設定
header('X-XSS-Protection:1; mode=block');

// HttpOnly属性 の指定
ini_set('session.cookie_httponly', 1);

// セッション開始
session_start();

// mb_send_mail のエンコーディング
mb_language('ja');

// 内部文字エンコーディングを設定
mb_internal_encoding('UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>送信画面</title>
</head>
<body>

<h1>お問い合わせ(送信画面)</h1>

<?php

// token セッション変数がセットされ、NULLまたは空文字
if(isset($_SESSION['token']) && !empty($_SESSION['token'])) {

    // POST['token']の値をtoken変数に代入
    $token = filter_input(INPUT_POST, 'token', FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW);

    // 各セッション変数を各変数に代入
    $name = isset($_SESSION['name']) && is_string($_SESSION['name']) ? mb_convert_encoding($_SESSION['name'], 'ISO-2022-JP-MS', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win') : '';
    $ruby = isset($_SESSION['ruby']) && is_string($_SESSION['ruby']) ? mb_convert_encoding($_SESSION['ruby'], 'ISO-2022-JP-MS', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win') : '';
    $mail = isset($_SESSION['mail']) && is_string($_SESSION['mail']) ? mb_convert_encoding($_SESSION['mail'], 'ISO-2022-JP-MS', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win') : '';
    $content = isset($_SESSION['content']) && is_string($_SESSION['content']) ? mb_convert_encoding($_SESSION['content'], 'ISO-2022-JP-MS', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win') : '';

    // トークンの値が一致しない場合は、エラー文を表示し、一致する場合は送信する
    if($token !== $_SESSION['token']) {

        echo '<p>送信後に再度アクセスされたか、お問い合わせの手順に誤りがあります。<br>お手数ですが、最初の画面からご入力ください。<p>';

    } else {

        /*  運営側へ送信するメールの設定  */

        // 送信先のメールアドレス
        $to      = '□□□□□@□□□□□□□';
        // 件名
        $subject = 'お問い合わせからの送信';
        // 本文
        $message = "◆お名前\n$name\n\n◆フリガナ\n$ruby\n\n◆メールアドレス\n$mail\n\n◆内容\n$content";
        // オプション
        $option = '-f'. $to;


        /*  問い合わせされた方へ自動返信するメールの設定  */

        // 件名
        $auto_subject = 'お問い合わせを承りました';
        // 送信元のメールアドレス
        $auto_from    = 'From:' . $to;
        // 本文
        $auto_message =
"※このメールは自動返信によるものです。

$name 様

このたびは、お問合せいただき、誠にありがとうございました。

お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。
";

        /* セーフモードがONの場合は、mb_send_mailの第5引数が使えないため、処理を分岐して送信 */
        if(ini_get('safe_mode')) {

            /*  運営側と自動返信のメールの送信が完了したら、送信完了の文章を表示する  */
            if(mb_send_mail($to, $subject, $message, "From:$mail") && mb_send_mail($mail, $auto_subject, $auto_message, $auto_from)) {
                echo '<p>このたびは、お問合せいただき、誠にありがとうございました。<br>お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。</p>';
            } else {
                echo '<p>大変申し訳ございませんが、メールの送信に失敗しました。<br>お手数ですが最初からやり直してください。</p>';
            }

        } else {

            /*  運営側と自動返信のメールの送信が完了したら、送信完了の文章を表示する  */
            if(mb_send_mail($to, $subject, $message, "From:$mail", $option) && mb_send_mail($mail, $auto_subject, $auto_message, $auto_from, $option)) {
                echo '<p>このたびは、お問合せいただき、誠にありがとうございました。<br>お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。</p>';
            } else {
                echo '<p>大変申し訳ございませんが、メールの送信に失敗しました。<br>お手数ですが最初からやり直してください。</p>';
            }

        }

    }

    // セッションの破棄
    $_SESSION = array();
    session_destroy();

} else {
    echo '<p>送信後に再度アクセスされたか、お問い合わせの手順に誤りがあります。<br>お手数ですが、最初の画面からご入力ください。</p>';
}

?>

</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

これ本当に化けませんか?
運営側へのメールの際、UTF-8とISO-2022-JP-MSが混在しているんですけど・・・
コードがiso-2022-jp-msで書かれていればいいんですが、そんなはずないですよね?
わたしの認識が間違っているのかな?(mb_internal_encoding('UTF-8');だしね)

content-typeにiso-2022-jp-msという指定はありえないんじゃないでしょうか。
何せ、iso-2022-jpにMicrosoftが独自拡張したものですから、Windows上でiso-2022-jpの文字コードのデータが来たらiso-2022-jp-msとして取り扱いますよね。

ubuntu上のthunderbirdはiso-2022-jpといいながら、丸数字もちゃんと出ます。
なので、大体のメーラーはiso-2022-jpときたら、iso-2022-jp-msと解釈しているんじゃないでしょうか?

追記

実験コードを書いてみました。

<?php
mb_language('ja');
mb_internal_encoding('UTF-8');

$text = '①②㈱髙アイウエオ';
$text = mb_convert_encoding($text, 'ISO-2022-JP-MS', 'UTF-8');

$body = "これがテストです\n\n$text"; // ※1

$output = mb_convert_encoding($body, 'ISO-2022-JP', 'UTF-8'); // ※2
echo mb_convert_encoding($output, 'UTF-8', 'ISO-2022-JP-MS'); // ※3

順を追います。

  1. ※1の部分では、UTF-8とISO-2022-JP-MSの混合文が出来ています。
  2. ※2の部分では、ISO-2022-JP-MSの部分($text変数の中身)はUTF-8ではないのでコンバートされません。
    その他の部分がISO-2022-JPにコンバートされます。
  3. ※3の部分では、ISO-2022-JPの部分コンバートされなかったISO-2022-JP-MS部分の両方がUTF-8へとコンバートされて出力されます。
    メールだとこの表示される形式へのコンバートをメーラーがやるわけです。大抵のメーラーはISO-2022-JPをISO-2022-JPのMS拡張形式として処理しますので、ちゃんと表示されます。

したがって、メーラーだとちゃんと出力される、という訳です。
しかしながら、文字コードを混在のままコンバートさせていいのかどうか、悩むところです。
もし、ISO-2022-JP-MSに統一したとしても、mb_send_mailはUTF-8からISO-2022-JPへのコンバートをしようとしますので、そうれもどうなんでしょうか?

mb_send_mailの代わりにmailを使うのも手ですが、subjectをmb_encode_mimeheaderする必要があるので面倒ですか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/02 17:59 編集

    返信が遅くなりすいません。いろいろ調べていました。

    ■神サイト見つけました

    ・PHP の mbstring に関するメモ
    http://www.asahi-net.or.jp/~wv7y-kmr/memo/php_mbstring.html

    ・「はしご高」を含むメールを送信したい
    https://teratail.com/questions/51786


    mb_language('ja'); と mb_internal_encoding('UTF-8'); の記述について、きちんと理解していなかったので、調べた結果の私の認識です。

    ■mb_language()

    下記のように記述してmb_get_infoでどのようになるのか確認しました。

     <?php
     mb_language('ja');
     mb_internal_encoding('UTF-8');
     var_dump(mb_get_info());

    mb_language('ja'); を指定することにより、メールヘッダを下記のように設定していました。

     mail_charset => ISO-2022-JP
     mail_header_encoding => BASE64
     mail_body_encoding => 7bit

    これがメールヘッダに記述される内容かと思います。

    ■mb_send_mail()

    > ヘッダと本文は mb_language() の設定に基づき変換、エンコードされます。
    > 引用:http://php.net/manual/ja/function.mb-send-mail.php


    mb_language('ja')としているため、今回は ISO-2022-JP にエンコードされます。

    ■ISO-2022-JP-MSでコンバートされた文字もメーラー側で表示する

    追記頂いたコードを参考に混在した場合、どのようになるか確認しました。
    追記頂いた通り、ISO-2022-JP-MSでコンバートされた文字も、「Content-Type: text/plain; charset=ISO-2022-JP」ならメーラー側で表示されました。

    【mb_language('ja') のパターン】

     <?php
     header("Content-type: text/html; charset=utf-8");
     mb_language('ja');
     mb_internal_encoding('UTF-8');
     $to = '□□□□□@□□□□□□□';
     $headers = 'From: □□□□□@□□□□□□□' . "\r\n";
     $subject = 'これはテストプログラムから送信したメール!';
     $text = '①②㈱髙アイウエオ';
     $text = mb_convert_encoding($text, 'ISO-2022-JP-MS', 'UTF-8');
     $body = "これがテストです\n\n$text";
     mb_send_mail ($to, $subject, $body, $headers);


     (結果)

    これがテストです

    ①②㈱髙アイウエオ

    【mb_language('uni') のパターン】

     <?php
     header("Content-type: text/html; charset=utf-8");
     mb_language('uni');
     mb_internal_encoding('UTF-8');
     $to = '□□□□□@□□□□□□□';
     $headers = 'From: □□□□□@□□□□□□□' . "\r\n";
     $subject = 'これはテストプログラムから送信したメール!';
     $text = '①②㈱髙アイウエオ';
     $text = mb_convert_encoding($text, 'ISO-2022-JP-MS', 'UTF-8');
     $body = "これがテストです\n\n$text";
     mb_send_mail ($to, $subject, $body, $headers);


     (結果)

    これがテストです

    ?$B-!-"-j|b?(I12345?(B


    ■今はUTF-8でもいいのですか?

    ISO-2022-JP-MS に拘りがあるわけではなく、特殊な文字も受け取れたらいいなということで指定していました。
    何の端末で受信するかにもよるかと思いますが、ガラケーで受け取らないなら、もうUTF-8でいいんですかね?

    ・これからはメールで「﨑」や「髙」を使うことにします
    http://uonuma.biz/blog/17925

    UTF-8で受け取るなら、mb_language を uni に変更するだけで、mb_send_mail でも受け取れるので、問題なさそうならUTF-8にしようかと考えています。

    キャンセル

  • 2017/05/02 19:34

    今や主流はスマホかパソコンなので、UTF-8でもいいと思ますよ。

    キャンセル

  • 2017/05/02 19:54

    なるほど、参考になりました。
    回答ありがとうございました((_ _ (´ω` )ペコ

    キャンセル

+1

直接的な回答ではないですが…

WEB上で内容確認画面を出しているわけですから、実際に送信されるものが確認画面と同じであることをユーザーは当然期待します

故に、確認画面の文字コードとメールの文字コードはUTF-8に合わせるのが良いかと

古い携帯などでUTF-8のメールに対応していないものもありますが、今どきは無視していいのでは
あるいは、送信時に自分あてメールの文字コード指定できるようにするかですね

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/02 18:01

    回答ありがとうございます。
    少し調べてみましたが、仰る通り、PCとスマホだけで受け取る場合ならUTF-8でも問題なさそうな気がしました。

    参考にしたサイト
    ・これからはメールで「﨑」や「髙」を使うことにします
    http://uonuma.biz/blog/17925

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • PHPに関する質問
  • mb_convert_encoding で文字エンコーディングを変換したときに、メーラー側ではその一部分を指定された文字エンコーディングで表示するのですか?