PHPコーディング

SMTPソケットをオープンしてメール送信(ソケットオープン)

2010年01月20日
PHPからメールを送信するには色々な方法があります。

手っ取り早く日本語メールを送信するには【mb_send_mail】があります。
添付ファイルやhtmlメールを送信するには、【mail】を使ってヘッダなど書かけば可能です。
しかし、この【mail】関数、いちいちSMTPソケットを開いたり閉じたりするので、メールマガジン、ステップメールを配信するのには不向きのようです。
その上、sendmailやqmailなどがインストールされていて、PHPから使えないとメールが送信できないそうです。
更に更に、処理が重たいのです。

ソケットのオープン・クローズも制御するには直接ソケットをオープンしてメールを送信しなければいけないようです。

もちろんその辺りの無料のツールも出回ってます。有名どころではPEAR::Mail_Mime

利用してもいいのですが、なにかあったとき自分でちゃっちゃと直せないと困るので、自分で書いてみることにしました。

大まかな流れは、
1. ソケットオープン
2. メール送信
3. ソケットクローズ
です。

メールマガジンやステップメール配信の場合は2を繰り返します。

まずはソケットオープンです。

$smtp = "smtp.domain.xxx";
$port = 587;
メール送信時にも認証が必要になってから、ポート番号は587のことが多くなってます。
$id = "mail_id";
$pw = "smtp_pw";

$sock = @fsockopen($smtp, $port);
でオープンできます。

成功したら、
@fputs($sock, "EHLO ".$port."\r\n");
認証が必要になってからコマンドはEHLO。

ここでサーバーが色々な情報を返してきます。
そのうちの1つが、利用可能な認証方式。
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM
の形式で返ってきます。

利用できる認証方式は、PLAIN、LOGIN、DIGEST-MD5、CRAM-MD5、NTLM。
それぞれの認証方式は後にして、ここで問題が発生です。

while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
として、サーバーから返された文字列を取得しようとしたのですが、どうやら無限ループに陥っているようで、タイムアウトになってしまいます。

調べていくと、【feof($sock)】でfalseが返ってきていないようです。【fsockopen】でオープンしたストリームでも使えるのに・・・なぜ?PHPのバグ??それとも終了しないとeofが返ってこないのか???

仕方ないので、別の方法に切替です。

while ($result = @fgets($sock, 128)) {
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
で一応解決です。
でも、ものすごく時間がかかってしまいます。
時間がかかっているのは、【$result = @fgets($sock, 128)】の部分です。
文字列が返ってくる場合は早いのですが、falseが返ってくるのに時間がかかっています。

PHPのマニュアルを読むと、【feof】関数はタイムアウトになったときもfalseを返すとのこと。

そこで、タイムアウトまでの時間を短くし、処理時間の短縮を試みました。

【stream_set_timeout】でタイムアウトまでの時間を設定できます。
タイムアウトになったら【stream_get_meta_data】で値が取得できます。

stream_set_timeout ($sock, 5);    //タイムアウトまでの時間を5秒に設定
while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $info = stream_get_meta_data($sock);
  if($info['timed_out'] == true){  //タイムアウトになったらこの値がtrueになる
    break;
  }
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}

5秒ではなくもっと小さな値にすればもっと早くなります。

これで解決!のようです。動かしているうちまた不都合が見つかるかもしれませんが。

長くなったので、smtp認証はまた次回。
コメントの書き込み
コメント