メール本文の1行の文字数制限 -PHPアプリ- | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

最近システム構築中に気づいた事なんですが・・・。


アプリケーションでよく、ユーザーが入力したデータをメールで送信するような機能があります。

Webアプリケーションの場合、テキストボックスやテキストエリア、ラジオボタンで選択された内容など。


この中で、テキストボックスやテキストエリアの内容をメールの本文に出力し送信する場合、1行の文字数(Byte数)に注意が必要です。

(もちろんアプリ側でデータを加工して送信する場合なども)


メール本文の1行は、改行コードを除いて998Byte以下を必須とし、78文字以下に抑える事が推奨されています。


RFC 2822 和訳


2.1.1. Line Length Limits

There are two limits that this standard places on the number of characters in a line. Each line of characters MUST be no more than 998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.

特にテキストエリアなどは、ユーザーが長文を打ち込むことが可能ですし、右端で勝手に折り返すので改行しているのか、単に折り返しているだけなのかその判断がつきにくいです。

ですので、折り返しただけで改行していない文字列をそのままメールの本文として送信すると本文が文字化けしたりします。


私は、PHP+Postfixの環境でこのような現象が発生しました。

PHPでは、912Byte目でどうも無理やり改行コードを挿入しているようです。

(PHPのmail関数が挿入しているのか、Postfixが送信時に挿入しているのかは不明ですが)

つまり、912ByteがASCIIコード以外の文字であった場合、文字化けが発生します。

mail関数mb_send_mail関数 どちらを使用しても913Byte目以降が文字化けしました)

912Byte目がASCIIコードの文字であった場合は、単純に改行されるだけなので、文字化けは発生しません。


このような事を回避するためにメール送信時に一工夫し、推奨の78文字以下に抑えられるようにロジックを作ってみました。


概要を簡単に説明すると

・ 78Byte以上の行のみを対象

・ 78Byte以上の行は、ダブルバイトの文字を含め39文字目の後に改行コード(CR+LF)

  を挿入する。

という簡単なものです。


つまり、1行の文字数がASCIIのみなら最大78文字、ダブルバイトのみなら最大39文字となるように変更します。

/**
* $bodyがメールの本文
*/

// 改行(一行)ごとにデータを取得する
$line = mb_split("\n", $body);
$body_tmp = NULL;
$line_length = 0;

// 1行あたりの制限文字数(日本語を取り扱う前提) 39*2 = 78 Byte
$part_length = 39;

for ($i = 0; $i < count($line); $i++) {
    $line_length = strlen($line[$i]);
    $one_line = NULL;

// ASCII文字のみであれば、最大制限文字数の2倍の文字数までを許可する
    if ($line_length > ($part_length * 2)) {
        $mb_length = mb_strlen($line[$i]);

// メール全体の行数を求める
        if (($mb_length % $part_length) == 0) {
            $loop_cnt = $mb_length / $part_length;
        } else {
            $loop_cnt = ceil(mb_strlen($line[$i]) / $part_length);
        }

        $start_num = 0;

// 1行ごとに制限文字数内で分解して改行コードを挿入する
        for ($j = 1; $j <= $loop_cnt; $j++) {
// 制限文字数単位で改行コード挿入
            $one_line .= mb_substr($line[$i], $start_num, $part_length) . "\r\n";
           $start_num = $part_length * $j;
        }
    } else {
        $one_line = $line[$i] . "\r\n";
    }
    $body_tmp .= $one_line;
}

RedHatES3+Apache1.3.37+PHP4.3.11上で動作しました。
組み込む際は、自己責任でお願いします。


また、あくまで行を分割するだけのロジックなので、主要なメールを送信すると言う部分は除いています。

もし、メールに添付ファイルを付けるのであれば添付ファイルを付ける処理の前に上記の分割処理をする必要があります。