本帖最后由 yqsphp 于 2019-3-20 13:30 编辑
很早之前自己写了一个php定时任务的类,不必用到windows或linux的定时计划任务,基于thinkphp3.2
我就不采用原生的PHP了,你们可以自己动手改成原生的,前端页面任务列表如何实现大家自己去做,我只提供后端的
原理:根据设置的最大执行时间默认30s,在程序执行到超过零界点时在重新请求_sock()这个方法,如此循环,永久执行(这里用到一个配置文件控制定时开关),已经跟浏览器无关了
问题:在非安全模式下即php.ini配置中:safe_mode= Off时设置 set_time_limit(0),测试n次,在window非安全模式下这个函数设置为0没什么用,在linux下可以。
因为反复调用自己,所有这个类中要不要set_time_limit(0)都无所谓了,但是睡眠时间不能超过最大执行时间
前端页面直接传值改变配置文件的值来控制定时开关,比如传 off = 1,以下代码中的定时任务的url做的是死的,前端定时任务列表可以直接传URL必须带host配置文件就是一个 return 1或0源码如下:[PHP] 纯文本查看 复制代码 <?php
namespace Admin\Controller;
use Think\Controller;
/**
* 事件定时任务
* start(),stop()这两个方法用来测试
*/
class TimerController extends Controller{
private static $url; //定时任务的url
public function _initialize(){
self::$url = 'http://'.$_SERVER['HTTP_HOST'].U('timer/app');
}
/**
* 定时器任务,通过前端传值控制,
* 启动后就跟浏览器无关了,除非服务重启
* 增加定时配置文件timer.php
*/
public function index(){
if(IS_POST){
$off = I('post.off');
//启动定时
if($off){
file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 1;');
$this->_sock(self::$url);
}else{
file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 0;');
}
}else{
$flag = include CONF_PATH.'timer.php';
$this->assign('status',$flag);
$this->display();
}
}
/**
* 启动定时器
*/
public function start(){
$this->_sock(self::$url);
}
/**
* 停止定时器
* 控制定时器这里采用修改文件真假值(timer.php)
*/
public function stop(){
$flag = require_once CONF_PATH.'timer.php';
file_put_contents(CONF_PATH.'timer.php', '<?php '.PHP_EOL.'return 0;');
}
/**
* 执行定时任务操作
*/
private function app(){
ignore_user_abort(true); //忽略浏览器关闭
set_time_limit(0); //设置超时 在window非安全模式下这个函数设置为0没什么用,在linux下可以。-_-
$flag = include CONF_PATH.'timer.php';
if(!$flag) die('stop');
/**
* 这里是逻辑代码区,由自己实现
*/
sleep(60); //睡眠时间,但不要超过最大执行时间,可以设置set_time_limit的值,建议不要设置0,200,300都行
$this->_sock(self::$url);//循环执行
}
/**
* 解析url,执行定时任务是防止阻塞
*
* [url=home.php?mod=space&uid=952169]@Param[/url] $url 执行任务的url
* [url=home.php?mod=space&uid=155549]@Return[/url] array|boolean
*/
protected function _sock($url){
//获取url主机
$host = parse_url($url, PHP_URL_HOST);
//获取url端口并判断是否80
$port = parse_url($url, PHP_URL_PORT) ?: 80;
//获取URL协议http或https
$scheme = parse_url($url, PHP_URL_SCHEME);
//获取host之后的url
$path = parse_url($url, PHP_URL_PATH);
//获取传递参数获取的是?后面的
$query = parse_url($url, PHP_URL_QUERY);
//将获取的参数拼接到url后面
if($query) $path .= '?' . $query;
//如果是https请求就改ssl协议请求
if($scheme == 'https'){
$host = 'ssl://' . $host;
}
//$a = ['host'=>$host,'port'=>$port,'$scheme'=>$scheme,'$path'=>$path,'$query'=>$query];
//dump($a);
//打开一个网络socket连接
$fp = fsockopen($host, $port, $error_code, $error_msg, 1);
if(! $fp){
return [
'error_code' => $error_code,
'error_msg' => $error_msg
];
}else{
// 开启了手册上说的非阻塞模式
stream_set_blocking($fp, 0);
// 设置超时
stream_set_timeout($fp, 1);
$header = "GET $path HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Connection: close\r\n\r\n"; // 长连接关闭
fwrite($fp, $header);
usleep(1000); // 如果没有这延时,可能在nginx服务器上就无法执行成功
fclose($fp);
return true;
}
}
}
下面的方法是自己现在用的仅供学习参考
[PHP] 纯文本查看 复制代码 /**
* 任务提醒
*/
private function app(){
//每日早上8点清空S
if(date('H') == 'O8') S('send_data',null);
$old_data = S('send_data');
//file_put_contents('data', $old_data.PHP_EOL,FILE_APPEND);
$child = M('userContact')->where('ccnexttime <> "" and isgood = 1 and ccremind <> 0 and ccissend = 0')->order('ccctime desc')->buildSql();
$cont = M('userContact')->field('a.*,u.mail')->table($child.' a')->join('left join '.C('DB_PREFIX').'admin u on u.truename = a.ccuser')->group('a.userid')->order('a.ccctime desc')->select();
//事先存储查询的数据,当再次循环查询是比较存储数据与查询数据是否一样
$new_data = md5(serialize($cont));
S('send_data',$new_data,1800);
if($old_data != $new_data){
foreach($cont as $val){
if((strtotime($val['ccnexttime']) - time()) <= $val['ccbefore']*60){
switch ($val['ccremind']){
case 1:
$data['wechat'][] = $val['ccuser']."你有待回访客户,请登录系统查看\n";
break;
case 2:
$data['email'][$val['mail']][] = $val['ccuser'].'你有待回访客户,请登录系统查看<br/>';
break;
case 3:
$data['mess'][] = $val['ccuser']."你有待回访客户,请登录系统查看\n";
break;
}
}
}
//邮箱提醒
if(!empty($data['email'])){
foreach($data['email'] as $key=>$v){
send_mail( $key, 'abcdef@qq.com', '任务提醒', implode(',', $v));
}
}
//微信提醒
if(!empty($data['wechat'])){
$chat = Wechat::app(1);
$data = [
'touser'=>'关注微信号的原始id' ,//ocanwvquBsmKIabcvElB1KJpcNAEg-QM
'msgtype'=>'text',
'text'=>['content'=>implode(',', $data['wechat'])]
];
//$chat->sendMassMessage($data);
$chat->sendCustomMessage($data);
vendor('Wechat.error');
file_put_contents('wx.txt', \Error::getError($chat->errCode).PHP_EOL,FILE_APPEND);
}
//短信提醒
if(!empty($data['mess'])){
$sms = new Sms(C('sms_require'), C('sms_port'), C('sms_version'));
$datas = $sms->sendTemplateSMS('你的电话号码', $data['mess'], 1);
}
}
}
|