主文件: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'] = <?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);
}
}