T

LOHAS的SMTP

またもや最悪なタイトル。

どういうわけか、SMTPでデバッグ用のコマンド出力をしないとlocalhost宛に送信したメールがconnection lostになってしまうことがあった。

デバッグ用に出力してあげるとなぜか大丈夫になる。なんだよ、もっとゆっくりやれってのかよ。お前それがLOHASとかいって喜んでるんじゃないだろうね。

そもそもPHPのmail関数は鬼門だ。携帯向けサービスの場合、先方が独自仕様だったりするので、出力を完全に制御できないmailコマンドでは正常に送信できないことが多々ある。PEARのMailで実装すると、文字コードやsubjectの扱いが経験の浅いプログラマには少々閾が高くなってしまう。日本語専門のクラスを実装するとマルチリンガル環境の構築が面倒だ。汎用的に作ると、テンプレートクラスを用意して日本語だったらインプリメンテーション部分はこのファイルを呼び出す、みたいな構成になるのかな。まずはちゃっちゃっと書いてみる:

class smtp{
    /*
     * 接続クラス
     * @return string(デバッグ用)
    */
    function connect($server, $port='25', $mode='', 
                     $timeout='10', $debug=FALSE){
	   if($mode){
                switch($mode){
                
                case "tsl":
                    $server = 'tsl://' . $server;
                    break;
                    
                case "ssl":
                    $server = 'ssl://' . $server;
                    break;
                
                default:
                    break;
                }
        }
        $this->fp = @fsockopen($server, $port, $errno, $errstr, $timeout);
        $out = "HELO ".$server."\r\n";
        if($this->fp){
            fwrite($this->fp, $out);
            $ret = fread($this->fp, '1024');
        }else{
            if($debug){
                $ret = 'command: ' . $out . "\n" . 'errno: ' 
                        . $errno . "\nerrstr: " . $errstr . "\n";
                echo $ret;
                return $ret;
            }
        }
        return $ret;
    }


    /*
     * コマンド実行
     * @param string $command
     * @param boolean debug
     * @param boolean nl2br
     * @return string
    */

    function sendCommand($command, $debug=FALSE){
        
        if(!$this->fp){
            $ret = "Connection Lost\n";
            if($debug){
                echo $ret;
                return $ret;
            }
        }else{
            fwrite($this->fp, $command);
            $ret = fread($this->fp, '1024');
            return $ret;
        }
        
    }
    
    /*
     * 接続解除クラス
     * @return void
    */
    function close(){
        if($this->fp){
            fclose($this->fp);
        }
    }
    
    //あと、subjectとbody生成用のユーティリティを用意しておく
    //実装するにはこれをextendする

    /*
     * subject処理
     * @param string $subject
     * @param string $enc
     * @return void
    */
    function makeSubject(&$subject, $enc){
        $subject = $subject;
    }
    
    /*
     * subject処理
     * @param string $body
     * @param string $enc
     * @return void
    */
    function makeBody(&$body, $enc){
        $body = $body;
    }
}

ちゃんと自分で文字コード処理ができる人は拡張なしで動くと思う。下の例はlocalhostでSMTPサーバと繋がる場合。

$from = 'you@your.domain';
$to = 'someone@someones.domain';

//subject
//EUC-JPでスクリプトを書いている場合
$subject = 'これは標題だよ';
$subject = mb_convert_encoding($subject, 'JIS', 'EUC-JP');
$subject = '=?ISO-2022-JP?B?' . base64_encode($subject) . '?=';

//body
$body_text = 'これは本文だよ';
$body_text = mb_convert_encoding($body_text, 'JIS', 'EUC-JP');

//ヘッダ
//これがないとSoftBank端末で「続きを読む」みたいなリンクが
//出て全文を受信できない場合にちゃんと動かない
$header = 'Mime-Version: 1.0
Content-Type: text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit
From: '.$from.'
To: '.$to.'

Subject: ' . $subject;


//ヘッダを含むbody生成
$body = $body = $header . '
'.$body_text;

$mail = new smtp();
print($mail->connect('localhost'));
print($mail->sendCommand('MAIL FROM: <' . $from . ">\r\n", 1));
print($mail->sendCommand('RCPT TO: <' . $to . ">\r\n", 1));
print($mail->sendCommand('DATA' . "\r\n", 1));
print($mail->sendCommand($body . "\r\n.\r\n", 1));
print($mail->sendCommand('QUIT' . "\r\n", 1));
$mail->close();

拡張するとしたら、

  • 一回の接続でセッションを使い切るのをもっと効率よくする
  • 複数のメールアドレスへの送信を可能にする
  • CC、BCCも使いたい
  • しゃらくせえ、YAMLで書いたらプラグインが生成されるようにしろ

といったところか。

Posted by on 2月 12, 2007 in PHP

Comments

  • […] #追記:可能になったらしい。 […]

  • コメントを残す