Rumah >pembangunan bahagian belakang >tutorial php >php守候进程-发送队列邮件

php守候进程-发送队列邮件

WBOY
WBOYasal
2016-06-23 13:04:271164semak imbas

在linux系统下运行```./demo.php```注:只能在liunx系统下运行demo.conf```# demo.conf# daemon modedaemon yes# child numberchild_num 5# child user and groupuser nobodygroup nogroup# retry timesretry_times 3# pid filepid_file /tmp/demo.pid# log filelog_file /tmp/demo.log```demo.php```#!/usr/bin/env php<?phperror_reporting(0);include "SendMail.php";define("EXIT_SUCCESS", 		0);define("EXIT_FAILURE", 		1);define("DEMO_LOG_DEBUG", 	0);define("DEMO_LOG_INFO",  	1);define("DEMO_LOG_WARN",  	2);define("DEMO_LOG_ERR",   	3);// default config$config = array(	"daemon"		=> FALSE,	"child_num"		=> 1,	"user"			=> NULL,	"group"			=> NULL,	"retry_times"	=> 0,	"pid_file"		=> NULL,	"log"			=> STDERR,	);// parse config fileparse_config($config);if ($argc > 1) {	switch ($argv[1]) {	case "help":		exit("Usage: ./demo.php {start|restart|reload|quit}\n\n");	case "start":		break;	case "restart":	case "reload":	case "quit":		$fp = fopen($config["pid_file"], "r");		if (!$fp) {			exit("Can't open pid file '{$config["pid_file"]}'.\n");		}		$pid = fgets($fp, 6);		if ($pid) {			$pid = intval($pid);		}		fclose($fp);		if ($argv[1] == "restart" || $argv[1] == "quit") {			posix_kill($pid, SIGINT);			if ($argv[1] == "restart") {				unlink($config["pid_file"]);			} else {				exit(EXIT_SUCCESS);			}		} else {			posix_kill($pid, SIGHUP);			exit(EXIT_SUCCESS);		}		break;	}}if (file_exists($config["pid_file"])) {	exit("pid file '{$config["pid_file"]}' already exits.\n");}if ($config["daemon"]) {	if (pcntl_fork() > 0) {		// parent exit		exit(EXIT_SUCCESS);	}	// set session id	posix_setsid();	// write pid file	if ($config["pid_file"]) {		$fp = fopen($config["pid_file"], "w");		if (!$fp) {			exit("Can't open pid file '{$config["pid_file"]}'.\n");		}		if (!fputs($fp, posix_getpid())) {			exit("Can't write pid to file '{$config["pid_file"]}'.\n");		}		fclose($fp);	}}write_log(DEMO_LOG_INFO, "main process started");declare(ticks = 1);pcntl_signal(SIGTERM, SIG_IGN);pcntl_signal(SIGHUP,  "reload");pcntl_signal(SIGINT,  "quit");pcntl_signal(SIGCHLD, "handle_child");$childs = array();$reparse_config = FALSE;$run = TRUE;while ($run) {	if ($reparse_config) {		write_log(DEMO_LOG_INFO, "reparse config file");		parse_config($config);		posix_kill(0, SIGTERM);		$reparse_config = FALSE;	}	for ($i = count($childs); $i < $config["child_num"]; ++$i) {		if (($child_pid = pcntl_fork()) == 0) {			do_child($config);			exit(EXIT_SUCCESS);		}		write_log(DEMO_LOG_INFO, "start child process: %d", $child_pid);		$childs[$child_pid] = 1;	}	sleep(1);}// kill childposix_kill(0, SIGTERM);// remove pid file@unlink($config["pid_file"]);write_log(DEMO_LOG_INFO, "main process exit");exit(EXIT_SUCCESS);function reload() {	$GLOBALS["reparse_config"] = TRUE;}function quit() {	$GLOBALS["run"] = FALSE;}function handle_child() {	while (($child_pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {		write_log(DEMO_LOG_INFO, "exit child process: %d", $child_pid);		unset($GLOBALS["childs"][$child_pid]);	}}function parse_config(Array &$config) {	$fp = fopen("demo.conf", "r");	if (!$fp) {		exit("Can't open config file.\n");	}	$lineno = 0;	while (($line = fgets($fp, 1024)) !== FALSE) {		++$lineno;		$line = trim($line);		if (!$line || $line[0] == "#") {			continue;		}		$params = preg_split("/\s+/", $line);		switch (strtolower($params[0])) {		case "daemon":			if ($params[1] == "yes") {				$config["daemon"] = TRUE;			} else if ($params[1] == "no") {				$config["daemon"] = FALSE;			} else {				$err = "daemon value must be 'yes' or 'no'";				goto parse_failed;			}			break;		case "child_num":			$child_num = intval($params[1]);			if ($child_num < 0 || $child_num > 1024) {				$err = "invalid child_num value '{$params[1]}'";				goto parse_failed;			}			$config["child_num"] = $child_num;			break;		case "retry_times":			$retry_times = intval($params[1]);			if ($retry_times < 1 || $retry_times > 100) {				$err = "invalid retry_times value '{$params[1]}'";				goto parse_failed;			}			$config["retry_times"] = $retry_times;			break;		case "user":			$user = posix_getpwnam($params[1]);			if (!$user) {				$err = "invalid user value '{$params[1]}'";				goto parse_failed;			}			$config["user"] = $user["uid"];			break;		case "group":			$group = posix_getgrnam($params[1]);			if (!$group) {				$err = "invalid group value '{$params[1]}'";				goto parse_failed;			}			$config["group"] = $group["gid"];			break;		case "pid_file":			$config["pid_file"] = $params[1];			break;		case "log_file":			$log = fopen($params[1], "a");			if (!$log) {				$err = "Can't open log file '{$params[1]}'";				goto parse_failed;			}			$config["log"] = $log;			break;		}		continue;	parse_failed:		fprintf(STDERR, "\n*** FATAL CONFIG FILE ERROR ***\n");		fprintf(STDERR, "Reading the configuration file, at line %d\n", $lineno);		fprintf(STDERR, ">>> '%s'\n", $line);		fprintf(STDERR, "%s\n", $err);		exit(EXIT_FAILURE);	}	fclose($fp);}function write_log($level, $fmt) {	$chars = ".-*#";	if (func_num_args() > 2) {		$args = func_get_args();		$err  = vsprintf($args[1], array_slice($args, 2));	} else {		$err = $fmt;	}	fprintf($GLOBALS["config"]["log"], "[%d] [%s] %s %s\n", 			posix_getpid(), date("Y-m-d H:i:s"), $chars[$level], $err);}function do_child() {	global $run;	$user 		 = $GLOBALS["config"]["user"];	$group 		 = $GLOBALS["config"]["group"];	$retry_times = $GLOBALS["config"]["retry_times"];	if ($user) {		posix_setuid($user);	}	if ($group) {		posix_setuid($group);	}	pcntl_signal(SIGTERM, "quit");	$redis = new Redis;	$redis->pconnect("127.0.0.1", 6379);	while ($run) {		try {			$email = $redis->lpop("email_queue");			if ($email) {				for ($i = 0; $i < $retry_times; ++$i) {					if (do_sendmail($email)) {						write_log(DEMO_LOG_INFO, "send mail to '%s' success", $email);						break;					}					write_log(DEMO_LOG_ERR, "send mail to '%s' failed, try again", $email);				}				if ($i == $retry_times) {					write_log(DEMO_LOG_ERR, "send mail to '%s' failed", $email);				}			}		} catch (RedisException $e) {			write_log(LOG_ERR, "receive message failed: %s", $e->getMessage());			exit(EXIT_FAILURE);		}		sleep(1);	}	exit(EXIT_SUCCESS);}function do_sendmail($email) {	$mail = new SendMail();	$mail->setServer("smtp.exmail.qq.com", "dingpeilong@xywy.com", "PLDing1989.com", 465, 1);	$mail->setFrom("dingpeilong@xywy.com");	$mail->setReceiver("77676182@qq.com");	$mail->setMail("test", "**hello world!**");	return $mail->sendMail();}```SendMail.php```<?php/*** 邮件发送类* 支持发送纯文本邮件和HTML格式的邮件,可以多收件人,多抄送,多秘密抄送,带附件(单个或多个附件),支持到服务器的ssl连接* 需要的php扩展:sockets、Fileinfo和openssl。* 编码格式是UTF-8,传输编码格式是base64* @example* $mail = new SendMail();* $mail->setServer("smtp@126.com", "XXXXX@126.com", "XXXXX"); //设置smtp服务器,普通连接方式* $mail->setServer("smtp.gmail.com", "XXXXX@gmail.com", "XXXXX", 465, true); //设置smtp服务器,到服务器的SSL连接* $mail->setFrom("XXXXX"); //设置发件人* $mail->setReceiver("XXXXX"); //设置收件人,多个收件人,调用多次* $mail->setCc("XXXX"); //设置抄送,多个抄送,调用多次* $mail->setBcc("XXXXX"); //设置秘密抄送,多个秘密抄送,调用多次* $mail->addAttachment("XXXX"); //添加附件,多个附件,调用多次* $mail->setMail("test", "**test**"); //设置邮件主题、内容* $mail->sendMail(); //发送*/class SendMail {    /**    * @var string 邮件传输代理用户名    * @access protected    */    protected $_userName;    /**    * @var string 邮件传输代理密码    * @access protected    */    protected $_password;    /**    * @var string 邮件传输代理服务器地址    * @access protected    */    protected $_sendServer;    /**    * @var int 邮件传输代理服务器端口    * @access protected    */    protected $_port;    /**    * @var string 发件人    * @access protected    */    protected $_from;    /**    * @var array 收件人    * @access protected    */    protected $_to = array();    /**    * @var array 抄送    * @access protected    */    protected $_cc = array();    /**    * @var array 秘密抄送    * @access protected    */    protected $_bcc = array();    /**    * @var string 主题    * @access protected    */    protected $_subject;    /**    * @var string 邮件正文    * @access protected    */    protected $_body;    /**    * @var array 附件    * @access protected    */    protected $_attachment = array();    /**    * @var reource socket资源    * @access protected    */    protected $_socket;    /**    * @var reource 是否是安全连接    * @access protected    */    protected $_isSecurity;    /**    * @var string 错误信息    * @access protected    */    protected $_errorMessage;    /**    * 设置邮件传输代理,如果是可以匿名发送有邮件的服务器,只需传递代理服务器地址就行    * @access public    * @param string $server 代理服务器的ip或者域名    * @param string $username 认证账号    * @param string $password 认证密码    * @param int $port 代理服务器的端口,smtp默认25号端口    * @param boolean $isSecurity 到服务器的连接是否为安全连接,默认false    * @return boolean    */    public function setServer($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;    }    /**    * 设置发件人    * @access public    * @param string $from 发件人地址    * @return boolean    */    public function setFrom($from) {        $this->_from = $from;        return true;    }    /**    * 设置收件人,多个收件人,调用多次.    * @access public    * @param string $to 收件人地址    * @return boolean    */    public function setReceiver($to) {        $this->_to[] = $to;        return true;    }    /**    * 设置抄送,多个抄送,调用多次.    * @access public    * @param string $cc 抄送地址    * @return boolean    */    public function setCc($cc) {        $this->_cc[] = $cc;        return true;    }    /**    * 设置秘密抄送,多个秘密抄送,调用多次    * @access public    * @param string $bcc 秘密抄送地址    * @return boolean    */    public function setBcc($bcc) {        $this->_bcc[] = $bcc;        return true;    }    /**    * 设置邮件附件,多个附件,调用多次    * @access public    * @param string $file 文件地址    * @return boolean    */    public function addAttachment($file) {        if(!file_exists($file)) {            $this->_errorMessage = "file " . $file . " does not exist.";            return false;        }        $this->_attachment[] = $file;        return true;    }    /**    * 设置邮件信息    * @access public    * @param string $body 邮件主题    * @param string $subject 邮件主体内容,可以是纯文本,也可是是HTML文本    * @return boolean    */    public function setMail($subject, $body) {        $this->_subject = base64_encode($subject);        $this->_body = base64_encode($body);        return true;    }    /**    * 发送邮件    * @access public    * @return boolean    */    public 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;    }    /**    * 返回错误信息    * @return string    */    public function error(){        if(!isset($this->_errorMessage)) {            $this->_errorMessage = "";        }        return $this->_errorMessage;    }    /**    * 返回mail命令    * @access protected    * @return array    */    protected function getCommand() {        $separator = "----=_Part_" . md5($this->_from . time()) . uniqid(); //分隔符        $command = array(                array("HELO sendmail\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);        }        //设置发件人        $command[] = array("MAIL FROM: <" . $this->_from . ">\r\n", 250);        $header = "FROM: <" . $this->_from . ">\r\n";        //设置收件人        if(!empty($this->_to)) {            $count = count($this->_to);            if($count == 1){                $command[] = array("RCPT TO: <" . $this->_to[0] . ">\r\n", 250);                $header .= "TO: <" . $this->_to[0] .">\r\n";            }            else{                for($i=0; $i<$count; $i++){                    $command[] = array("RCPT TO: <" . $this->_to[$i] . ">\r\n", 250);                    if($i == 0){                        $header .= "TO: <" . $this->_to[$i] .">";                    }                    elseif($i + 1 == $count){                        $header .= ",<" . $this->_to[$i] .">\r\n";                    }                    else{                        $header .= ",<" . $this->_to[$i] .">";                    }                }            }        }        //设置抄送        if(!empty($this->_cc)) {            $count = count($this->_cc);            if($count == 1){                $command[] = array("RCPT TO: <" . $this->_cc[0] . ">\r\n", 250);                $header .= "CC: <" . $this->_cc[0] .">\r\n";            }            else{                for($i=0; $i<$count; $i++){                    $command[] = array("RCPT TO: <" . $this->_cc[$i] . ">\r\n", 250);                    if($i == 0){                    $header .= "CC: <" . $this->_cc[$i] .">";                    }                    elseif($i + 1 == $count){                        $header .= ",<" . $this->_cc[$i] .">\r\n";                    }                    else{                        $header .= ",<" . $this->_cc[$i] .">";                    }                }            }        }        //设置秘密抄送        if(!empty($this->_bcc)) {            $count = count($this->_bcc);            if($count == 1) {                $command[] = array("RCPT TO: <" . $this->_bcc[0] . ">\r\n", 250);                $header .= "BCC: <" . $this->_bcc[0] .">\r\n";            }            else{                for($i=0; $i<$count; $i++){                    $command[] = array("RCPT TO: <" . $this->_bcc[$i] . ">\r\n", 250);                    if($i == 0){                    $header .= "BCC: <" . $this->_bcc[$i] .">";                    }                    elseif($i + 1 == $count){                        $header .= ",<" . $this->_bcc[$i] .">\r\n";                    }                    else{                        $header .= ",<" . $this->_bcc[$i] .">";                    }                }            }        }        //主题        $header .= "Subject: =?UTF-8?B?" . $this->_subject ."?=\r\n";        if(isset($this->_attachment)) {            //含有附件的邮件头需要声明成这个            $header .= "Content-Type: multipart/mixed;\r\n";        }        elseif(false){            //邮件体含有图片资源的,且包含的图片在邮件内部时声明成这个,如果是引用的远程图片,就不需要了            $header .= "Content-Type: multipart/related;\r\n";        }        else{            //html或者纯文本的邮件声明成这个            $header .= "Content-Type: multipart/alternative;\r\n";        }        //邮件头分隔符        $header .= "\t" . 'boundary="' . $separator . '"';        $header .= "\r\nMIME-Version: 1.0\r\n";        //这里开始是邮件的body部分,body部分分成几段发送        $header .= "\r\n--" . $separator . "\r\n";        $header .= "Content-Type:text/html; charset=utf-8\r\n";        $header .= "Content-Transfer-Encoding: base64\r\n\r\n";        $header .= $this->_body . "\r\n";        $header .= "--" . $separator . "\r\n";        //加入附件        if(!empty($this->_attachment)){            $count = count($this->_attachment);            for($i=0; $i<$count; $i++){                $header .= "\r\n--" . $separator . "\r\n";                $header .= "Content-Type: " . $this->getMIMEType($this->_attachment[$i]) . '; name="=?UTF-8?B?' . base64_encode( basename($this->_attachment[$i]) ) . '?="' . "\r\n";                $header .= "Content-Transfer-Encoding: base64\r\n";                $header .= 'Content-Disposition: attachment; filename="=?UTF-8?B?' . base64_encode( basename($this->_attachment[$i]) ) . '?="' . "\r\n";                $header .= "\r\n";                $header .= $this->readFile($this->_attachment[$i]);                $header .= "\r\n--" . $separator . "\r\n";            }        }        //结束邮件数据发送        $header .= "\r\n.\r\n";        $command[] = array("DATA\r\n", 354);        $command[] = array($header, 250);        $command[] = array("QUIT\r\n", 221);        return $command;    }    /**    * 发送命令    * @access protected    * @param string $command 发送到服务器的smtp命令    * @param int $code 期望服务器返回的响应吗    * @return boolean    */    protected function sendCommand($command, $code) {        //发送命令给服务器        try{            if(@socket_write($this->_socket, $command, strlen($command))){                //当邮件内容分多次发送时,没有$code,服务器没有返回                if(empty($code))  {                    return true;                }                //读取服务器返回                $data = trim(socket_read($this->_socket, 1024));                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();        }    }    /**    * 安全连接发送命令    * @access protected    * @param string $command 发送到服务器的smtp命令    * @param int $code 期望服务器返回的响应吗    * @return boolean    */    protected function sendCommandSecurity($command, $code) {        try {            if(fwrite($this->_socket, $command)){                //当邮件内容分多次发送时,没有$code,服务器没有返回                if(empty($code))  {                    return true;                }                //读取服务器返回                $data = trim(fread($this->_socket, 1024));                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();        }    }    /**    * 读取附件文件内容,返回base64编码后的文件内容    * @access protected    * @param string $file 文件    * @return mixed    */    protected function readFile($file) {        if(file_exists($file)) {            $file_obj = file_get_contents($file);            return base64_encode($file_obj);        }        else {            $this->_errorMessage = "file " . $file . " dose not exist";            return false;        }    }    /**    * 获取附件MIME类型    * @access protected    * @param string $file 文件    * @return mixed    */    protected function getMIMEType($file) {        if(file_exists($file)) {            $mime = mime_content_type($file);            /*if(! preg_match("/gif|jpg|png|jpeg/", $mime)){                $mime = "application/octet-stream";            }*/            return $mime;        }        else {            return false;        }    }    /**    * 建立到服务器的网络连接    * @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;    }}```

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn