[PHP-users 17014]Re: mb_send_mail()と mb_encode_mimeheader()

Shuji TANAKA stanaka @ longpro.ne.jp
2003年 7月 28日 (月) 20:39:18 JST


stanakaです.

 ご返事ありがとうございます.
 少しよく分からなかったのですが,

> ヘッダフィールドは、US-ASCIIで記述されることと言うのと、
> From:ヘッダに関しては、
>   From: [displayname] "<" addr-spec ">"
> といったような規定があるので "<" addr-spec ">"の部分をUS-ASCIIで無い
> 他文字コードとしてエンコードするのは拙いと思います。

 この部分は,私の不満に賛同していただけたと思っていいのでしょうか?.それと
も逆でしょうか?.


> で、そのコードはいつ見せてもらえますかね :)

 あれ?,なんか怒ってます?.
 delegate の付属品にそんなものがあるとは知りませんでした.PHPで書くより明ら
かに軽そうですね.
 とりあえず了解を頂いたものとして,以下にPHPコードを出させていただきます.
なんか,76バイトに収まるまで何度もmime_encode()を呼び出したり,mb_strwidth()
とか一字ごとに呼び出したりしてもうちょっとスマートな方法はないのか,と自分で
も思うのですが・・・・.
 どうかよろしくご指導のほどをお願いいたします.


-- ▽ここから
function _encode_mimeheader( $string, $recurse = false ){
  $lower_recurse = true;

  if( $string == "" )
    return "";

  $idx = 0;

  $length = mb_strlen( $string );

  // ループ:内部で条件判断でbreakして脱出する
  while( true ){

    // とりあえず全体をエンコードしてみる
    $estring = mime_encode( mb_substr( $string, 0, $length - $idx ),
$recurse );

    $ustring = mb_substr( $string, 0, $length - $idx );
    $cstring = mb_substr( $string, $length - $idx );

    $u_last = mb_substr( $ustring, -1, 1 );
    $c_top = mb_substr( $cstring, 0, 1 );
    $c_next = mb_substr( $cstring, 1, 1 );

    // エンコード結果が76バイトを超えるようであれば,
    // 文字列の後尾から一文字ずつ減らして
    // エンコーディングしなおしてみる:ループ継続
    if( mb_strwidth( $estring ) > 76 ){

      $idx++;
      continue;
    }

    // 全体が問題なくエンコードできた(76バイト以内)ので,
    // ループ脱出
    else if( $cstring == "" )
      break;

    // 先頭からのエンコード結果が76バイト以内に収まったが・・・
    else{

      // 改行を挿入する位置を発見できないまま,
      // 文字列の先頭まで戻ってきてしまったので,異常終了
      if( $length == $idx ) die( "_encode_mimeheader():
".$idx."/".$length.": Cannot Generate!" );

      // スペースではないASCII文字と,一スペースと
      // それに続くスペースではないASCII文字があれば,
      // スペースとその前の間を行の切れ目とする:ループ脱出
      if( isSpace( $c_top ) && isAscii( $u_last ) && !isSpace( $u_last ) &&
isAscii( $c_next ) && !isSpace( $c_next ) ){
        $lower_recurse = false;
        break;
      }

      // 非ASCII文字の間を行の切れ目とする:ループ脱出
      if( !isAscii( $c_top ) && !isAscii( $u_last ) ){
        break;
      }

      // 先頭の一文字前まできてしまったら,仕方がないので
      // そこを行の切れ目とする:ループ脱出
      if( $length == $idx + 1 )
        break;

      // ループ継続
      $idx++;
    }
  }

  // 非ASCII文字の間を行の切れ目とした場合,次行の先頭にスペースを置く
  if( $lower_recurse ) $line_top = " ";
  else $line_top = "";

  // 次行以降をエンコード
  // 先頭におくスペースはエンコード後,連結
  $cestring = $line_top._encode_mimeheader( $cstring, $lower_recurse );

  // 連結
  if( $cestring == "" )
    return $estring;
  else
    return $estring."\r\n".$cestring;
}

function mime_encode( $string, $enc_start = false ){

  $idx = 0;
  $ret_string = "";
  $s = "";

  if( $enc_start )
    $stat = "j";
  else
    $stat = "a";

  // 文字列の先頭から順に一文字ずつ取り出してループ
  while( ( $c = mb_substr( $string, $idx++, 1 ) ) != "" ){
    $p = mb_substr( $string, $idx -2, 1 );
    $n = mb_substr( $string, $idx, 1 );

    // 何のために入れたか忘れた
    // 最先頭でも最後尾でもなく,且つ非ASCII文字から
    // スペースでないASCII文字に移る間のにあるスペース文字は
    // 必ずASCII文字としてエンコードしないでおく
    if( $p != "" && $n != "" && !isAscii( $p ) && $c == " " && isAscii( $n )
&& !isSpace( $n ) ){
      $new_stat = "a";
    }
    // 通常スペース文字は,その前の文字と同じ
    // 文字エンコーディング(ASCII,非ASCII)として
    // MIMEエンコードする,しないを決める
    else if( $c == " " ){
      $s = $s." ";
      continue;
    }

    if( isAscii( $c ) )
      $new_stat = "a";
    if( !isAscii( $c ) )
      $new_stat = "j";

    // この文字が前の文字と同じエンコーディングであれば,
    // 前までの文字列と連結してループを継続
    if( $stat == $new_stat )
      $s = $s.$c;
    // この文字が前の文字までと違うエンコーディングであれば,
    // 前の文字までの列をエンコーディングするかしないか決める
    else{

      // 前の文字までがASCII文字の列の場合,
      // エンコーディングしないでそのまま連結
      if( $stat == "a" )
        $ret_string = $ret_string.$s;
      // 前の文字までが非ASCII文字の列の場合,
      // Bエンコードして連結
      else if( $stat == "j" )
        $ret_string = $ret_string."=?ISO-2022-JP?B?".base64_encode(
mb_convert_encoding( $s, "ISO-2022-JP" ) )."?=";

      // 新しい(この文字からの)文字エンコーディング
      $stat = $new_stat;
      // 新しく文字列の連結を始める
      $s = $c;
    }
  }
  // 最後に文字エンコーディングが変化してから,
  // 文字列の最後尾までのエンコードをして/しないで連結する
  if( $stat == "a" )
    $ret_string = $ret_string.$s;
  else
    $ret_string = $ret_string."=?ISO-2022-JP?B?".base64_encode(
mb_convert_encoding( $s, "ISO-2022-JP" ) )."?=";

  return $ret_string;
}

function isAscii( $char ){

  if( mb_strlen( $char ) == 0 )
    die( "isAscii(): Bad argument, the argument of function is an empty
string." );
  if( mb_strlen( $char ) != 1 )
    die( "isAscii(): Bad argument, the argument of function is not a string
of one charactor, but of multiple charactors." );

  if( mb_strwidth( $char ) == 1 )
    return true;
  else
    return false;
}

function isSpace( $char ){

  if( mb_strlen( $char ) == 0 )
    die( "isAscii(): Bad argument, the argument of function is an empty
string." );
  if( mb_strlen( $char ) != 1 )
    die( "isSpace(): Bad argument, the argument of function is not a string
of one charactor, but of multiple charactors." );

  if( $char == " " )
    return true;
  else
    return false;
}

-- △ここまで

 因みに比較ですが,簡単ですが以下のようになります.エンコードはそれぞれの関
数で,デコードはそれぞれの関数で行ったものをmb_decode_mimeheader()で行ってい
ます.

  元文字列
From: Japanese Name 日本語名 <youaddress @ your.domain>
  エンコード文字列:mb_encode_mimeheader()
From: Japanese Name =?ISO-2022-JP?B?GyRCRnxLXDhsTD4bKEIgPHlvdWFkZHJlc3NA?=
 =?ISO-2022-JP?B?eW91ci5kb21haW4+?=
  エンコード文字列:mime_encode()
From: Japanese Name =?ISO-2022-JP?B?GyRCRnxLXDhsTD4bKEI=?=
<youaddress @ your.domain>
  エンコード文字列:_encode_mimeheader()
From: Japanese Name =?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=
 =?ISO-2022-JP?B?GyRCTD4bKEI=?= <youaddress @ your.domain>

  デコード文字列:mb_encode_mimeheader()
From: Japanese Name 日本語名 <youaddress @ your.domain>
  デコード文字列:mime_encode()
From: Japanese Name 日本語名 <youaddress @ your.domain>
  デコード文字列:_encode_mimeheader()
From: Japanese Name 日本語名 <youaddress @ your.domain>






PHP-users メーリングリストの案内