123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- <?php
- namespace addons\crontab\controller;
- use addons\crontab\model\Crontab;
- use Cron\CronExpression;
- use fast\Http;
- use think\Controller;
- use think\Db;
- use think\Exception;
- use think\Log;
- /**
- * 定时任务接口
- *
- * 以Crontab方式每分钟定时执行,且只可以Cli方式运行
- * @internal
- */
- class Autotask extends Controller
- {
- /**
- * 初始化方法,最前且始终执行
- */
- public function _initialize()
- {
- // 只可以以cli方式执行
- if (!$this->request->isCli()) {
- $this->error('Autotask script only work at client!');
- }
- parent::_initialize();
- // 清除错误
- error_reporting(0);
- // 设置永不超时
- set_time_limit(0);
- }
- /**
- * 执行定时任务
- */
- public function index()
- {
- $time = time();
- $logDir = LOG_PATH . 'crontab' . DS;
- if (!is_dir($logDir)) {
- mkdir($logDir, 0755);
- }
- //筛选未过期且未完成的任务
- $crontabList = Crontab::where('status', '=', 'normal')->order('weigh DESC,id DESC')->select();
- $execTime = time();
- foreach ($crontabList as $crontab) {
- $update = [];
- $execute = false;
- if ($time < $crontab['begintime']) {
- //任务未开始
- continue;
- }
- if ($crontab['maximums'] && $crontab['executes'] > $crontab['maximums']) {
- //任务已超过最大执行次数
- $update['status'] = 'completed';
- } else {
- if ($crontab['endtime'] > 0 && $time > $crontab['endtime']) {
- //任务已过期
- $update['status'] = 'expired';
- } else {
- //重复执行
- //如果未到执行时间则继续循环
- $cron = CronExpression::factory($crontab['schedule']);
- if (!$cron->isDue(date("YmdHi", $execTime)) || date("YmdHi", $execTime) === date("YmdHi", $crontab['executetime'])) {
- continue;
- }
- $execute = true;
- }
- }
- // 如果允许执行
- if ($execute) {
- $update['executetime'] = $time;
- $update['executes'] = $crontab['executes'] + 1;
- $update['status'] = ($crontab['maximums'] > 0 && $update['executes'] >= $crontab['maximums']) ? 'completed' : 'normal';
- }
- // 如果需要更新状态
- if (!$update) {
- continue;
- }
- // 更新状态
- $crontab->save($update);
- // 将执行放在后面是为了避免超时导致多次执行
- if (!$execute) {
- continue;
- }
- $result = false;
- $message = '';
- try {
- if ($crontab['type'] == 'url') {
- if (substr($crontab['content'], 0, 1) == "/") {
- // 本地项目URL
- $message = shell_exec('php ' . ROOT_PATH . 'public/index.php ' . $crontab['content']);
- $result = $message ? true : false;
- } else {
- $arr = explode(" ", $crontab['content']);
- $url = $arr[0];
- $params = isset($arr[1]) ? $arr[1] : '';
- $method = isset($arr[2]) ? $arr[2] : 'POST';
- try {
- // 远程异步调用URL
- $ret = Http::sendRequest($url, $params, $method);
- $result = $ret['ret'];
- $message = $ret['msg'];
- } catch (\Exception $e) {
- $message = $e->getMessage();
- }
- }
- } elseif ($crontab['type'] == 'sql') {
- $ret = $this->sql($crontab['content']);
- $result = $ret['ret'];
- $message = $ret['msg'];
- } elseif ($crontab['type'] == 'shell') {
- // 执行Shell
- $message = shell_exec($crontab['content']);
- $result = $message ? true : false;
- }
- } catch (\Exception $e) {
- $message = $e->getMessage();
- }
- $log = [
- 'crontab_id' => $crontab['id'],
- 'executetime' => $time,
- 'completetime' => time(),
- 'content' => $message,
- 'status' => $result ? 'success' : 'failure',
- ];
- Db::name("crontab_log")->insert($log);
- }
- return "Execute completed!\n";
- }
- /**
- * 执行SQL语句
- */
- protected function sql($sql)
- {
- //这里需要强制重连数据库,使用已有的连接会报2014错误
- $connect = Db::connect([], true);
- $connect->execute("select 1");
- // 执行SQL
- $sqlquery = str_replace('__PREFIX__', config('database.prefix'), $sql);
- $sqls = preg_split("/;[ \t]{0,}\n/i", $sqlquery);
- $result = false;
- $message = '';
- $connect->startTrans();
- try {
- foreach ($sqls as $key => $val) {
- if (trim($val) == '' || substr($val, 0, 2) == '--' || substr($val, 0, 2) == '/*') {
- continue;
- }
- $message .= "\nSQL:{$val}\n";
- $val = rtrim($val, ';');
- if (preg_match("/^(select|explain)(.*)/i ", $val)) {
- $count = $connect->execute($val);
- if ($count > 0) {
- $resultlist = Db::query($val);
- } else {
- $resultlist = [];
- }
- $message .= "Total:{$count}\n";
- $j = 1;
- foreach ($resultlist as $m => $n) {
- $message .= "\n";
- $message .= "Row:{$j}\n";
- foreach ($n as $k => $v) {
- $message .= "{$k}:{$v}\n";
- }
- $j++;
- }
- } else {
- $count = $connect->getPdo()->exec($val);
- $message = "Affected rows:{$count}";
- }
- }
- $connect->commit();
- $result = true;
- } catch (\PDOException $e) {
- $message = $e->getMessage();
- $connect->rollback();
- $result = false;
- }
- return ['ret' => $result, 'msg' => $message];
- }
- }
|