<?php
// 这个函数的意思是 无论浏览器是否运行,这段代码仍然执行
// 在一个php文档的开头直接来一句:
// ignore_user_abort(true);
// 这时,通过url访问这个php的时候,即使用户把浏览器关掉(断开连接),php也会在服务器上继续执行。利用这个特性,我们可以实现非常牛的功能,也就是通过它来实现定时任务的激活,激活之后就随便它自己怎么办了,实际上就有点类似于后台任务。
// 而sleep(n)则是指当程序执行到这里时,暂时不往下执行,而是休息n秒钟。如果你访问这个php,就会发现页面起码要加载n秒钟。实际上,这种长时间等待的行为是比较消耗资源的,不能大量使用。
// 那么定时任务到底怎么实现呢?使用下面的代码即可实现:
ignore_user_abort(true);
set_time_limit(0);
date_default_timezone_set('PRC'); // 切换到中国的时间
$run_time = strtotime('+1 day'); // 定时任务第一次执行的时间是明天的这个时候
$interval = 3600*12; // 每12个小时执行一次
if(!file_exists(dirname(__FILE__).'/cron-run')) exit(); // 在目录下存放一个cron-run文件,如果这个文件不存在,说明已经在执行过程中了,该任务就不能再激活,执行第二次,否则这个文件被多次访问的话,服务器就要崩溃掉了
do {
if(!file_exists(dirname(__FILE__).'/cron-switch')) break; // 如果不存在cron-switch这个文件,就停止执行,这是一个开关的作用
$gmt_time = microtime(true); // 当前的运行时间,精确到0.0001秒
$loop = isset($loop) && $loop ? $loop : $run_time - $gmt_time; // 这里处理是为了确定还要等多久才开始第一次执行任务,$loop就是要等多久才执行的时间间隔
$loop = $loop > 0 ? $loop : 0;
if(!$loop) break; // 如果循环的间隔为零,则停止
sleep($loop);
// ...
// 执行某些代码
// ...
@unlink(dirname(__FILE__).'/cron-run'); // 这里就是通过删除cron-run来告诉程序,这个定时任务已经在执行过程中,不能再执行一个新的同样的任务
$loop = $interval;
} while(true);
// 通过执行上面这段php代码,即可实现定时任务,直到你删除cron-switch文件,这个任务才会停止。
// 但是有一个问题,也就是如果用户直接访问这个php,实际上没有任何作用,页面也会停在这个地方,一直处于加载状态,有没有一种办法可以消除这种影响呢?fsockopen帮我们解决了这个问题。
// fsockopen可以实现在请求访问某个文件时,不必获得返回结果就继续往下执行程序,这是和curl通常用法不一样的地方,我们在使用curl访问网页时,一定要等curl加载完网页后,才会执行curl后面的代码,虽然实际上curl也可以实现“非阻塞式”的请求,但是比fsockopen复杂的多,所以我们优先选择fsockopen,fsockopen可以在规定的时间内,比如1秒钟以内,完成对访问路径发出请求,完成之后就不管这个路径是否返回内容了,它的任务就到这里结束,可以继续往下执行程序了。利用这个特性,我们在正常的程序流中加入fsockopen,对上面我们创建的这个定时任务php的地址发出请求,即可让定时任务在后台执行。如果上面这个php的url地址是www.yourdomain.com/script.php,那么我们在编程中,可以这样:
// function _sock($url) {
// $host = parse_url($url,PHP_URL_HOST);
// $port = parse_url($url,PHP_URL_PORT);
// $port = $port ? $port : 80;
// $scheme = parse_url($url,PHP_URL_SCHEME);
// $path = parse_url($url,PHP_URL_PATH);
// $query = parse_url($url,PHP_URL_QUERY);
// if($query) $path .= '?'.$query;
// if($scheme == 'https') {
// $host = 'ssl://'.$host;
// }
// $fp = fsockopen($host,$port,$error_code,$error_msg,1);
// if(!$fp) {
// return array('error_code' => $error_code,'error_msg' => $error_msg);
// }
// else {
// stream_set_blocking($fp,true);//开启了手册上说的非阻塞模式
// stream_set_timeout($fp,1);//设置超时
// $header = "GET $path HTTP/1.1
";
// $header.="Host: $host
";
// $header.="Connection: close
";//长连接关闭
// fwrite($fp, $header);
// usleep(1000); // 这一句也是关键,如果没有这延时,可能在nginx服务器上就无法执行成功
// fclose($fp);
// return array('error_code' => 0);
// }
// }
// _sock('www.yourdomain.com/script.php');