主文件:MongoMailSend.php
包含文件:include.php
配置文件:config.php
自定义类库目录:library
目录结构:
主文件:scripts/MongoMailSend.php
包含文件:scripts/include.php
配置文件:scripts/config.php
自定义类库目录:
scripts/library
scripts/library/Hrs
scripts/library/Hrs/Mongo
数据库
mongodb数据库连接:scripts/library/Hrs/Mongo/Config.php
mongodb数据库邮件表的操纵(要继承与Table.php文件的):scripts/library/Hrs/Mongo/QueueMail.php
mongodb数据库操纵的类文件:scripts/library/Hrs/Mongo/Table.php
邮件
scripts/library/Hrs/Mail
发短信的类文件:scripts/library/Hrs/Mail/Sender.php
MongoMailSend.php
<?php /* * 从mongodb数据库读取邮件信息数据, 调用邮件发送接口发送邮件 * 具体实现文件 */ //加载include.php文件,包括文件,这样就能加载所有的文件了。include.php文件还包含一些方法,如锁文件的创建与删除 include_once 'include.php'; //设置锁文件,根据自己情况来,也可以不用锁的。get_temp_dir()方法在include.php文件中 //创建锁文件 //程序跑完再解锁,没跑完如果运行时间超过1800s,则重新上锁。 //没解锁,这个文件不能重复运行。 $lock_file = get_temp_dir().'/mongomailsend.lock'; //文件上锁 //lock_up($lock_file); //以smtp方式发送邮件,Zend_Mail_Transport_Smtp对象需要在send()方法被调用之前创建并注册到Zend_Mail中去。(!!!!!!) //创建Zend_Mail_Transport_Smtp对象$smtp_tp $smtp_tp = new Zend_Mail_Transport_Smtp($config['mail']['host'],$config['mail']['smtp']); //把$smtp_tp注册到Zend_Mail中 Zend_Mail::setDefaultTransport($smtp_tp); //把发送者邮箱与发送者名字也注册到Zend_Mail中 Zend_Mail::setDefaultFrom($config['mail']['from'],$config['mail']['fromname']); //mongodb数据库配置信息 $server = array('host'=>$config['databases']['mongodb']['host'], 'port'=>$config['databases']['mongodb']['port'], 'database'=>$config['databases']['mongodb']['database']); //设置mongodb配置文件中的配置信息,要识别Hrs_Mongo_Config类文件,需要include.php写一个自动加载类方法 Hrs_Mongo_Config::set($server); //new 邮件队列的对象 $queue = new Hrs_Mongo_QueueMail(); //是否开启密抄 $open_bcc = $config['mail']['open_bcc']; //发送失败的数目,默认为0,失败时自增加1。 $send_status = 0; //当前时间 $nowtime = time(); //测试数据 /* $arr = array( 'email'=>'xxx@163.com', 'subject'=>'mongo测试', 'content'=>'内容区域', 'attachments'=>'', 'status'=>0, 'fail_times'=>0, 'create_time'=>$nowtime, 'sent_time'=>0, 'mailfrom'=>'xxx@126.com', 'fromname'=>'xxx', 'replyto'=>''); $queue->insert($arr); */ //获取需要发送的队列信息 $result = $queue->getQueueMail(); /* *判断队列信息是否为空 *队列存在的话,是数组,foreach循环一下数组。 *send_mail()发邮件的函数 ,成功与否都返回一个状态值$status *$status true时发送成功 *$status不为true时,$send_status自增加1这个记录发送失败的数目 *注意!发送时有一个参数判断是否密抄的 $open_bcc *send_mail()参数讲解 *mail 接收者邮箱 *toname *subject邮件主题 *content邮件内容 *attachments附件 *mailfrom发件者邮箱 *fromname发件者名称 *replyto回复地址(可为空) *open_bcc是否密抄,用于系统监控 */ if (!empty($result)) { foreach($result as $row){ //如果数据库有发件人邮箱就用这个邮箱,没有就用配置文件默认的邮箱 //如果数据库有发件人名字就用这个名字,没有就用配置文件默认的名字 $mailfrom = $row['mailfrom'] ? $row['mailfrom'] : $config['mail']['mailfrom']; $fromname = $row['fromname'] ? $row['fromname'] : $config['mail']['fromname']; $status = send_mail($row['email'], $row['toname'], $row['subject'], $row['content'], $row['attachments'], $mailfrom, $fromname, $row['replyto'], $open_bcc); if($status == true){ $queue->changeMail(array('$set'=>array('status'=>1, 'sent_time'=>$nowtime)), $row['_id']); }else{ $send_status += 1; $queue->changeMail(array('$set'=>array('status'=>2,'sent_time'=>$nowtime), '$inc'=>array('fail_times'=>1)), $row['_id']); } } } else { echo 'no mail send'; } //判断最后发送情况 if($send_status){ echo "mail num=".$send_status."mail send fail\n"; }else if(!count($result)){ echo "no mail send,".date("Y-m-d H:i:s")."\n"; }else{ echo "mail send success\n"; } //文件解锁 //un_lock($lock_file);
include.php
<?php /* * 包括文件:包括的如下 * 数据库配置文件——》config.php * 自定义类库目录——》library目录 */ //设置时区,亚洲上海 date_default_timezone_set('Asia/Shanghai'); /* *加载config.php数据库配置文件 *__FILE__当前文件的路径 *dirname()返回路径中的目录部分 *dirname(__FILE__),返回当前文件的目录 */ include_once(dirname(__FILE__).'/config.php'); /* * 加载自定义类库(这里不同于Zend Framework) * 动态设置环境变量,把自定义类库放置到环境变量中 * SCRIPT_PATH脚本路径 * set_include_path动态设置环境变量 PATH_SEPARATOR路径分离器,我们的环境变量中的 ';' * realpath()返回绝对路径 注意,你必须有,否则返回空 * get_include_path获取环境变量 */ defined('SCRIPT_PATH')||define('SCRIPT_PATH', dirname(__FILE__)); set_include_path(implode(PATH_SEPARATOR, array( realpath(SCRIPT_PATH.'/library'), get_include_path() ))); /* * 文件上锁,锁文件主要也就是时间锁的问题。这里半个小时1800s * file_exists()判断文件是否存在 * file_get_contents()获取文件的内容 * 只要时间不超过1800s,文件就处于锁定状态 * 超过1800s,重新写锁文件的内容 * 也就是说1800s,运行时间,或者解锁。 */ function lock_up($file, $time=1800) { if(file_exists($file)){ $content = file_get_contents($file); if((intval(time())-intval($content)) > $time){ file_put_contents($file, time()); }else{ die('file is locked!'); } }else{ file_put_contents($file, time()); } } /* * 文件解锁,删除目录 * unlink(); */ function un_lock($file) { @unlink($file); } /* * 临时目录,存放脚本锁文件位置 * strncmp()比较两个字符串 WIN下就存放在 C:\WINDOWS\TEMP * getenv()系统的环境变量 */ function get_temp_dir() { if(strncmp(PHP_OS, 'WIN', 3)===0){ return getenv('TEMP'); }else{ return '/tmp'; } } /* * 格式化邮件 * 处理附近问题 * @param string $att */ function attaformat($att){ /* * 当存在附件时,无论一个附件或N个附件都要以数组的形式再进行序列化.直接对单一附件进行序列化将导致反序列化后发送邮件的错误 * example: * 文件一=array('filename','mimeType','disposition','encoding'); * right: serialize(array('文件1')); * error: serialize('文件1'); */ $attArr = ""; $attArr = unserialize($att); return is_array($attArr) ? $attArr : ''; } /* * 发送邮件 * @param string $tomail #收信人邮件 * @param string $toName #收信人名称 * @param string $subject #邮件标题 * @param string $bodyText #邮件内容 * @param string $attachments #邮件附件 * @param string $mailfrom #发送人邮件 * @param string $fromname #发送人名称 * @param string $replyto #回复邮件地址 * @param string $open_bcc #是否打开密送 * @return boolean */ function send_mail($tomail,$toName,$subject,$bodyText,$attachments=NULL,$mailfrom="",$fromname="",$replyto="", $open_bcc=false){ global $config; $bcc_mail = $config['mail']['bcc_mail']; $attachment = attaformat($attachments); $status = true; try{ $email = new Hrs_Mail_Sender('UTF-8'); $email->setHeaderEncoding(Zend_Mime::ENCODING_BASE64); if($mailfrom) $email->setFrom($mailfrom, $fromname); if($replyto) $email->setReplyTo($replyto, $fromname); //是否密抄(系统监控用) if($open_bcc && $bcc_mail) $email->addBcc($bcc_mail); $email->addTo($tomail,$toName) ->setSubject(html_entity_decode($subject, 2, 'UTF-8')) ->setBodyHtml($bodyText); if($attachment){ $attaNum = count($attachment); for($j=0;$j<$attaNum;$j++){ if(!isset($attachment[$j]['body'])) { echo "可能有一个以上附件格式不正确被抛弃。\n"; continue; } $email->createAttachment( $attachment[$j]['body'], $attachment[$j]['mimeType'], $attachment[$j]['disposition'], $attachment[$j]['encoding'], $attachment[$j]['filename']); //默认二进制文件 base64编码传输 } } $email->send(); echo "mail success\n\r"; }catch (Exception $e){ //邮件发送失败 echo "mail fail:".$e->getMessage()."\n"; $status = false; } unset($email); return $status; } /* * 自动加载类方法 使Hrs_Mongo_Config可以识别 * class_exists() 类是否已定义 * interface_exists() 接口是否已定义 * str_replace() 字符串替换函数 * $filename文件名(不带后缀) * 加载类文件 require_once(); */ function __autoload($class) { if(class_exists($class, false) || interface_exists($class, false)){ return ; } try { $filename = str_replace('_', '/', $class); @require_once ($filename.'.php'); if(!class_exists($class, false) || !interface_exists($class, false)){ throw new Exception('Class ' . $class . ' not found'); } } catch (Exception $e) { return ; } }
config.php
<?php /* * 数据库配置文件 * host * port */ $config['databases']['mongodb']['host'] = '172.16.26.240'; $config['databases']['mongodb']['port'] = '27088'; $config['databases']['mongodb']['database'] = 'local'; /* * mail smtp配置 * mail host 发送邮件服务器 * mail smtp port smtp端口号(25是smtp标准端口号) * mail smtp auth login(验证用户名与口令)(login登录意思) * mail smtp username 用户名 * mail smtp password 口令 * mail from 发送者邮箱(或企业邮箱)(邮箱的后缀就是mail host) * mail fromname 发送者名字(或企业名字) * mail open_bcc 是否开启密抄 true是开启,false是关闭 * mail bcc_mail 密抄邮箱 * 注意!密抄功能是监控的需要。 */ $config['mail']['host'] = '126.com'; $config['mail']['smtp']['port'] = '25'; $config['mail']['smtp']['auth'] = 'login'; $config['mail']['smtp']['username'] = 'xxxxx'; $config['mail']['smtp']['password'] = 'xxxxx'; $config['mail']['from'] = 'xxx@126.com'; $config['mail']['fromname'] = 'XXX'; $config['mail']['open_bcc'] = false; //是否开启密抄 true是开启,false是关闭 $config['mail']['bcc_mail'] = 'xxxxx@126.com'; //密抄邮箱
library/Hrs/Mongo/Config.php
<?php require_once 'Zend/Exception.php'; class Hrs_Mongo_Config { const VERSION = '1.7.0'; const DEFAULT_HOST = 'localhost'; const DEFAULT_PORT = 27017; private static $host = self::DEFAULT_HOST ; private static $port = self::DEFAULT_PORT ; private static $options = array( 'connect' => true, 'timeout' => 30, //'replicaSet' => '' //If this is given, the master will be determined by using the ismaster database command on the seeds ); public static $conn = ''; public static $defaultDb = ''; public static $linkStatus = ''; public static function set($server = 'mongodb://localhost:27017', $options = array('connect' => true)) { if(!$server){ $url = 'mongodb://'.self::$host.':'.self::$port; } if(is_array($server)){ if(isset($server['host'])){ self::$host = $server['host']; } if(isset($server['port'])){ self::$port = $server['port']; } if(isset($server['user']) && isset($server['pass'])){ $url = 'mongodb://'.$server['user'].':'.$server['pass'].'@'.self::$host.':'.self::$port; }else{ $url = 'mongodb://'.self::$host.':'.self::$port; } } if(is_array($options)){ foreach (self::$options as $o_k=>$o_v){ if(isset($options[$o_k])) self::$options[$o_k] = $o_v; } } try{ self::$conn = new Mongo($url, self::$options); self::$linkStatus = 'success'; }catch (Exception $e){ self::$linkStatus = 'failed'; } if(isset($server['database'])){ self::selectDB($server['database']); } } public static function selectDB($database){ if($database){ try { if(self::$linkStatus=='success') self::$defaultDb = self::$conn->selectDB($database); return self::$defaultDb; } catch(InvalidArgumentException $e) { throw new Zend_Exception('Mongodb数据库名称不正确'); } }else{ throw new Zend_Exception('Mongodb数据库名称不能为空'); } } }
library/Hrs/Mongo/QueueMail.php
<?php require_once 'Hrs/Mongo/Table.php'; /* mongo表结构对应mysql表结构如下: CREATE TABLE `queue_mails` ( `qid` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'PK', `email` varchar(100) NOT NULL COMMENT '邮件地址', `subject` varchar(255) NOT NULL COMMENT '邮件主题', `content` longtext NOT NULL COMMENT '邮件内容', `attachments` mediumblob COMMENT '邮件附件,最大16M。多个附件使用分隔符<!--attachment-->分隔。附件名称与附件内容之间用<!--separator-->分隔', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '发送状态,0:未发送,1:已发送,2:发送失败', `fail_times` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数', `create_time` int(10) unsigned NOT NULL COMMENT '创建时间', `sent_time` int(10) unsigned NOT NULL COMMENT '发送时间', `mailfrom` varchar(100) NOT NULL COMMENT '发送者邮箱', `fromname` varchar(100) NOT NULL COMMENT '发送者名称', `replyto` varchar(100) NOT NULL COMMENT '发送者的接受邮箱', PRIMARY KEY (`qid`) ) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='邮件队列' */ class Hrs_Mongo_QueueMail extends Hrs_Mongo_Table { protected $_name = 'mails'; protected $_row = array( 'email' => '', 'subject' => '', 'content' => '', 'attachments' => '', 'status' => 0, 'fail_times' => 0, 'create_time' => 0, 'sent_time' => 0, 'mailfrom' => '', 'fromname' => '', 'replyto' => '' ); public function addRow($data){ $prepareData = array(); foreach($this->_row as $key=>$val){ if(isset($data[$key])){ $prepareData[$key] = $data[$key]; }else{ $prepareData[$key] = $val; } } $this->insert($prepareData); } public function getQueueMail(){ return $this->find(array('status'=>array('$in'=>array(0,2)),'fail_times'=>array('$lt'=>4)))->toArray(); } public function changeMail($data, $_id){ $result = $this->update($data, array('_id'=>new MongoId($_id))); return $result; } public function deleteMail($_id){ $result = $this->delete(array('_id'=>new MongoId($_id))); } }
library/Hrs/Mongo/Table.php
<?php require_once 'Hrs/Mongo/Config.php'; abstract class Hrs_Mongo_Table { protected $_db = ''; protected $_name = ''; protected $_data = array(); protected $c_options = array( 'fsync'=>true, 'safe'=>true ); protected $u_options = array( //'upsert'=>false, 'multiple'=>true, 'fsync'=>true, 'safe'=>true ); /* protected $r_options = array( );*/ protected $d_options = array( 'fsync'=>true, 'justOne'=>false, 'safe'=>true ); protected function _setAdapter($database=''){ if(!$database) throw new Zend_Exception('Mongodb数据库名称不能为空'); Hrs_Mongo_Config::selectDB($database); } public function __construct() { if(Hrs_Mongo_Config::$conn instanceof Mongo){ $name = $this->_name; $defDb = Hrs_Mongo_Config::$defaultDb; $this->_db = $defDb->$name; }else{ throw new Zend_Exception('Mongodb服务器连接失败'); } } public function insert($data){ if(!$this->testLink()) return false; $ret = $this->_db->insert($data, $this->c_options); return $ret; } public function update($data, $where){ if(!$this->testLink()) return false; return $this->_db->update($where, $data, $this->u_options); } public function find($where=array(),$limit=0){ if($this->testLink()) { if($limit>0){ $this->_data = $where ? $this->_db->find($where)->limit($limit)->snapshot() : $this->_db->find()->limit($limit)->snapshot(); }else{ $this->_data = $where ? $this->_db->find($where)->limit($limit)->snapshot() : $this->_db->find()->limit($limit)->snapshot(); } } return $this; } //find cursor public function look($where=array(),$fields=array()){ if($this->testLink()) { if($fields){ return $where ? $this->_db->find($where,$fields): $this->_db->find()->fields($fields); }else{ return $where ? $this->_db->find($where) : $this->_db->find(); } } return false; } public function delete($where){ if(!$this->testLink()) return false; return $this->_db->remove($where, $this->d_options); } public function dropMe(){ if(!$this->testLink()) return false; return $this->_db->drop(); } public function __toString(){ return $this->_data; } public function toArray(){ $tmpData = array(); foreach($this->_data as $id=>$row){ $one_row = array(); foreach($row as $key=>$col){ $one_row[$key] = $col; } $one_row['_id'] = $id; $tmpData[] = $one_row; } return $tmpData; } protected function testLink(){ return Hrs_Mongo_Config::$linkStatus == 'success' ? true :false; } }
library/Hrs/Mail/Sender.php
<?php include_once 'Zend/Mail.php'; class Hrs_Mail_Sender extends Zend_Mail { public function setSubject($subject) { if ($this->_subject === null) { $subject = $this->_filterOther($subject); $this->_subject = $this->_hrs2_encodeHeader($subject); $this->_storeHeader('Subject', $this->_subject); } else { /** * @see Zend_Mail_Exception */ require_once 'Zend/Mail/Exception.php'; throw new Zend_Mail_Exception('Subject set twice'); } return $this; } private function _hrs2_encodeHeader($value){ if (Zend_Mime::isPrintable($value) === false) { if ($this->getHeaderEncoding() === Zend_Mime::ENCODING_QUOTEDPRINTABLE) { $value = Zend_Mime::encodeQuotedPrintableHeader($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND); } else { $value = $this->_hrs2_encodeBase64Header($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND); } } return $value; } private function _hrs2_encodeBase64Header($value, $charset, $lineLength, $lineEnd){ $prefix = '=?' . $charset . '?B?'; $suffix = '?='; $remainingLength = $lineLength - strlen($prefix) - strlen($suffix); $encodedValue = $this->_hrs2_encodeBase64($value, $remainingLength, $lineEnd); $encodedValue = str_replace($lineEnd, $suffix . $lineEnd . ' ' . $prefix, $encodedValue); $encodedValue = $prefix . $encodedValue . $suffix; return $encodedValue; } private function _hrs2_encodeBase64($str, $lineLength, $lineEnd){ $lineLimit = floor($lineLength * 3 /4); $encoding = mb_detect_encoding($str); $encodedLines = array(); $line = 0; while(strlen($str)>0){ $encodedLine = mb_strcut($str, 0, $lineLimit, $encoding); $encodedLines[] = base64_encode(rtrim($encodedLine)); $str = rtrim(mb_strcut($str.' ', $lineLimit, strlen($str), $encoding)); $line++; } return implode($lineEnd, $encodedLines); } }