PHP支持SSL的SocketSMTP类

email.class.php

//需要的php扩展:sockets和openssl
class smtp
{
    protected $_userName;
    protected $_password;
    protected $_sendServer;
    protected $_port;
    protected $_from;
    protected $_fromName;
    protected $_to = array();
    protected $_subject;
    protected $_body;
    protected $_socket;
    protected $_isSecurity;
    protected $_errorMessage;

    function __construct($server, $username = "", $password = "", $port = 25, $isSecurity = false)
    {
        $this->_sendServer = $server;
        $this->_port = $port;
        $this->_isSecurity = $isSecurity;
        $this->_userName = empty($username) ? "" : base64_encode($username);
        $this->_password = empty($password) ? "" : base64_encode($password);
        return true;
    }

    function setMail($from, $name, $to, $subject, $body)
    {
        $this->_from = $from;
        $this->_fromName = $name;
        $this->_to = $to;
        $this->_subject = $subject;
        $this->_body = $body;
        return true;
    }

    protected function getCommand()
    {
        $command = array(
            array("HELO DESMG\r\n", 250)
        );
        if (!empty($this->_userName)) {
            $command[] = array("AUTH LOGIN\r\n", 334);
            $command[] = array($this->_userName . "\r\n", 334);
            $command[] = array($this->_password . "\r\n", 235);
        }

        $header = "MIME-Version: 1.0\r\n";
        $header .= "Content-Type:text/html\r\n";
        $header .= "Date: " . date("r") . "\r\n";
        //$header .= "Message-ID: <" . date("YmdHis") . "." . uniqid() . "." . $this->_from . ">\r\n";
        $header .= "FROM: " . $this->_fromName . "<" . $this->_from . ">\r\n";
        $header .= "TO: <" . $this->_to . ">\r\n";
        $header .= "Subject: " . $this->_subject . "\r\n";
        $header .= "\r\n" . $this->_body;
        $header .= "\r\n.\r\n";
        $command[] = array("MAIL FROM: <" . $this->_from . ">\r\n", 250);
        $command[] = array("RCPT TO:<" . $this->_to . ">\r\n", 250);
        $command[] = array("DATA\r\n", 354);
        $command[] = array($header, 250);
        $command[] = array("QUIT\r\n", 221);
        return $command;
    }

    function sendMail()
    {
        $command = $this->getCommand();
        $this->_isSecurity ? $this->socketSecurity() : $this->socket();
        foreach ($command as $value) {
            $result = $this->_isSecurity ? $this->sendCommandSecurity($value[0], $value[1]) : $this->sendCommand($value[0], $value[1]);
            if ($result) {
                continue;
            } else {
                return false;
            }
        }
        //其实这里也没必要关闭,smtp命令:QUIT发出之后,服务器就关闭了连接,本地的socket资源会自动释放
        $this->_isSecurity ? $this->closeSecutity() : $this->close();
        return true;
    }

    protected function sendCommand($command, $code)
    {
        echo 'Send command:' . $command . ',expected code:' . $code . '<br />';
        //发送命令给服务器
        try {
            if (socket_write($this->_socket, $command, strlen($command))) {
                //当邮件内容分多次发送时,没有$code,服务器没有返回
                if (empty($code)) {
                    return true;
                }
                //读取服务器返回
                $data = trim(socket_read($this->_socket, 1024));
                echo 'response:' . $data . '<br /><br />';
                if ($data) {
                    $pattern = "/^" . $code . "+?/";
                    if (preg_match($pattern, $data)) {
                        return true;
                    } else {
                        $this->_errorMessage = "Error:" . $data . "|**| command:";
                        return false;
                    }
                } else {
                    $this->_errorMessage = "Error:" . socket_strerror(socket_last_error());
                    return false;
                }
            } else {
                $this->_errorMessage = "Error:" . socket_strerror(socket_last_error());
                return false;
            }
        } catch (Exception $e) {
            $this->_errorMessage = "Error:" . $e->getMessage();
        }
    }

    protected function sendCommandSecurity($command, $code)
    {
        echo 'Send command:' . $command . ',expected code:' . $code . '<br />';
        try {
            if (fwrite($this->_socket, $command)) {
                //当邮件内容分多次发送时,没有$code,服务器没有返回
                if (empty($code)) {
                    return true;
                }
                //读取服务器返回
                $data = trim(fread($this->_socket, 1024));
                echo 'response:' . $data . '<br /><br />';

                if ($data) {
                    $pattern = "/^" . $code . "+?/";
                    if (preg_match($pattern, $data)) {
                        return true;
                    } else {
                        $this->_errorMessage = "Error:" . $data . "|**| command:";
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                $this->_errorMessage = "Error: " . $command . " send failed";
                return false;
            }
        } catch (Exception $e) {
            $this->_errorMessage = "Error:" . $e->getMessage();
        }
    }

    function error()
    {
        if (!isset($this->_errorMessage)) {
            $this->_errorMessage = "";
        }
        return $this->_errorMessage;
    }

    /**
     * 建立到服务器的网络连接
     * @access protected
     * @return boolean
     */
    protected function socket()
    {
        //创建socket资源
        $this->_socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));

        if (!$this->_socket) {
            $this->_errorMessage = socket_strerror(socket_last_error());
            return false;
        }

        socket_set_block($this->_socket);//设置阻塞模式

        //连接服务器
        if (!socket_connect($this->_socket, $this->_sendServer, $this->_port)) {
            $this->_errorMessage = socket_strerror(socket_last_error());
            return false;
        }
        $str = socket_read($this->_socket, 1024);
        if (!preg_match("/220+?/", $str)) {
            $this->_errorMessage = $str;
            return false;
        }

        return true;
    }

    /**
     * 建立到服务器的SSL网络连接
     * @access protected
     * @return boolean
     */
    protected function socketSecurity()
    {
        $remoteAddr = "tcp://" . $this->_sendServer . ":" . $this->_port;
        $this->_socket = stream_socket_client($remoteAddr, $errno, $errstr, 30);
        if (!$this->_socket) {
            $this->_errorMessage = $errstr;
            return false;
        }

        //设置加密连接,默认是ssl,如果需要tls连接,可以查看php手册stream_socket_enable_crypto函数的解释
        stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);

        stream_set_blocking($this->_socket, 1); //设置阻塞模式
        $str = fread($this->_socket, 1024);
        if (!preg_match("/220+?/", $str)) {
            $this->_errorMessage = $str;
            return false;
        }

        return true;
    }

    /**
     * 关闭socket
     * @access protected
     * @return boolean
     */
    protected function close()
    {
        if (isset($this->_socket) && is_object($this->_socket)) {
            $this->_socket->close();
            return true;
        }
        $this->_errorMessage = "No resource can to be close";
        return false;
    }

    /**
     * 关闭安全socket
     * @access protected
     * @return boolean
     */
    protected function closeSecutity()
    {
        if (isset($this->_socket) && is_object($this->_socket)) {
            stream_socket_shutdown($this->_socket, STREAM_SHUT_WR);
            return true;
        }
        $this->_errorMessage = "No resource can to be close";
        return false;
    }
}