このエントリをはてなブックマークに追加このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをYahoo!ブックマークに追加このエントリをFC2ブックマークに追加このエントリをNifty Clipに追加このエントリをPOOKMARK. Airlinesに追加このエントリをBuzzurl(バザール)に追加このエントリをChoixに追加このエントリをnewsingに追加

日本語によるメール送信

フレームワークを使っていると足下がおろそかになってしまいそうな心配がぬぐえません(本当はフレームワークのせいではなくスキルの未熟さから来ているのでしょうが)。

今回はメール送信を通じて日本語を扱う際の設定を見直してみました。

PHP で日本語を扱う際には mbstring 関連のディレクティブを設定することになりますが。大抵は次のような設定になると思います。

mbstring.language = Japanese
mbstring.internal_encoding = EUC-JP

名前から察するに mbstring.language によって mbstring 関連の関数が日本語に初期化され、 mbstring.internal_encoding によって mbstring 関連の関数が内部で利用する文字コードが決定されると思ってしまいがちですが、これが大きな誤解で、 mbstring.language は mb_send_mail() が送信の際に参考にする言語で、 mbstring.internal_encoding は mbstring 系の関数のデフォルトのエンコーディング程度の意味しかないとのことです。

幸い、この程度であれば誤解のまま運用し続けても大きな問題は起こらないかもしれませんが、 mbstring.internal_encoding という名前から PHP も Java のように「内部エンコーディング」があると勘違いしてしまうと、出所のわからないバグに遭遇してしまうかもしれません。 PHP が扱っている(マルチバイト)の文字列も所詮はただのバイト列という再認識が必要かと思われます。

今回得た知識を利用した関数が次のコードです。パソコン向けに添付ファイルを同梱しないメールを送るという用途には十分かと思われますが、おそらく PHPMailer の利用を検討した方が賢明です。

<?php
/**
 * 日本語によるメールを送信する。
 *
 * 日本語によるメールを送信します。
 * メールの送信に際して、データを次のように加工します。
 *
 *   - 件名 ($subject) を MIME エンコーディングを施す
 *   - 件名 ($subject) が長くなる場合に改行する
 *   - 件名 ($subject) の文字コードを JIS へ変換する
 *   - 本文 ($body) の文字コードを JIS へ変換する
 *   - 差出人 ($fromName) の文字コードを JIS へ変換する
 *
 * 引数の文字コードは mbstring.internal_encoding と同じである必要があります。
 * 別の文字コードを指定したい場合は $encoding に文字コード名を渡します。
 * もし $encoding を指定した場合は、一時的に mbstring.internal_encoding の
 * 設定値を $encoding へ変更しますが、関数の終了時に元の値へ戻します。
 *
 * また、この関数は次の用途には利用できません。
 *
 *   - モバイル環境へ対してメール送信したい場合
 *   - 添付ファイルを伴う(マルチパート)メール送信したい場合
 *   - Return-Path の設定を保証したい場合
 *
 * このような場合は次のスクリプトの利用を検討してください。
 *
 *   - http://phpmailer.codeworxtech.com/
 *   - http://techblog.ecstudio.jp/tech-tips/mail-japanese-advance.html
 *
 * ■必要な php.ini ディレクティブ
 *   - sendmail_path        [U/ ] sendmail へのパスとパラメータ
 *   - SMTP                 [ /W] SMTP サーバの IP アドレス
 *   - smtp_port            [ /W] SMTP サーバのポート
 *   - sendmail_from        [ /W] メールの From 部と Return-Path 部のアドレス
 *
 * ■必要の応じて設定する php.ini ディレクティブ
 *   - mb_internal_encoding [U/W] 引数データのデフォルトエンコーディング
 *
 * @access public
 * @param  string  $to         メールの宛先アドレス
 * @param  string  $subject    メールの件名
 * @param  string  $body       メールの本文
 * @param  string  $from       メールの差出人アドレス
 * @param  string  $fromName   メールの差出人の名前
 * @param  string  $returnPath メールの Return-Path ヘッダ
 * @param  string  $encoding   引数データのエンコーディング
 * @return boolean             MTA へのデータ送信が成功したかの真偽値
 */
function btaSendMailByJapanese($to, $subject, $body,
                               $from = null, $fromName = null,
                               $returnPath = null,
                               $encoding = null)
{
    assert('function_exists("mb_send_mail")');

    // PHP ディレクティブを回復できるように現在の値を待避する
    {
        $previousLanguage = ini_get("mbstring.language");
        $previousEncoding = ini_get("mbstring.internal_encoding");
        $previousWinFrom = ini_get("sendmail_from");
    }

    // 日本語メールを送信するために PHP ディレクティブを変更する
    {
        mb_language("Japanese");
        if (!is_null($encoding)) {
            mb_internal_encoding($encoding);
        }
    }

    // 追加ヘッダを構築
    $header = "";
    {
        $headers = array();

        // From ヘッダを構築
        if (!empty($from)) {
            if (!empty($fromName)) {
                $fromNameEncoding = mb_detect_encoding($fromName);
                $fromName = mb_convert_encoding($fromName, "JIS", $fromNameEncoding);
                $fromName = mb_encode_mimeheader($fromName);
                array_push($headers, sprintf("From:%s <%s>", $fromName, $from));
            } else {
                array_push($headers, sprintf("From:%s", $from));
            }
        }

        // mb_send_mail() の第4引数に空文字を渡すと
        // ヘッダが分断されて残りが本文となってしまうため、
        // 必ず第4引数が作られるようにする
        if (count($headers) == 0) {
            array_push($headers, "X-PlaceHolder:placeholder");
        }

        // 追加ヘッダを mb_send_mail() へ渡せる形式へ変換する
        $header = join("\r\n", $headers);
    }

    // MTA パラメータを構築
    $mtaOption = "";
    {
        // Return-Path の指定
        if (!empty($returnPath)) {
            $mtaOption = "-f ". $returnPath;
            ini_set("sendmail_from", $from);
        }
    }

    // メールを送信する
    $result = mb_send_mail($to, $subject, $body, $header, $mtaOption);

    // 変更した PHP ディレクティブを回復する
    {
        ini_set("mbstring.language", $previousLanguage);
        ini_set("mbstring.internal_encoding", $previousEncoding);
        ini_set("sendmail_from", $previousWinFrom);
    }

    return $result;
}
?>
back to top