znt/module/cron/model.php

353 lines
12 KiB
PHP
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* The model file of cron module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package cron
* @version $Id$
* @link https://www.zentao.net
*/
class cronModel extends model
{
/**
* 通过ID获取定时任务。
* Get by Id.
*
* @param int $cronID
* @access public
* @return object
*/
public function getById(int $cronID): object
{
return $this->dao->select('*')->from(TABLE_CRON)->where('id')->eq($cronID)->fetch();
}
/**
* Get crons.
*
* @param string $params
* @access public
* @return array
*/
public function getCrons(string $params = ''): array
{
return $this->dao->select('*')->from(TABLE_CRON)
->where('1=1')
->beginIF(strpos($params, 'nostop') !== false)->andWhere('status')->ne('stop')->fi()
->beginIF($this->config->edition != 'max')
->andWhere('command')->ne('moduleName=measurement&methodName=initCrontabQueue')
->andWhere('command')->ne('moduleName=measurement&methodName=execCrontabQueue')
->fi()
->fetchAll('id', false);
}
/**
* Parse crons.
*
* @param array $crons
* @access public
* @return array
*/
public function parseCron(array $crons): array
{
$this->app->loadClass('crontab', true);
$parsedCrons = array();
foreach($crons as $cron)
{
$row = "{$cron->m} {$cron->h} {$cron->dom} {$cron->mon} {$cron->dow} {$cron->command}";
preg_match_all('/(\S+\s+){5}|.*/', $row, $matches);
if($matches[0])
{
try
{
$runTime = isset($cron->datetime) ? $cron->datetime : date(DT_DATETIME1);
$parsedCron = array();
$parsedCron['schema'] = trim($matches[0][0]);
$parsedCron['command'] = trim($matches[0][1]);
$parsedCron['cron'] = crontab::factory($parsedCron['schema']);
$parsedCron['time'] = $parsedCron['cron']->getNextRunDate($runTime);
$parsedCrons[$cron->id] = $parsedCron;
}
catch(InvalidArgumentException $e)
{
$this->dao->update(TABLE_CRON)->set('status')->eq('stop')->where('id')->eq($cron->id)->exec();
continue;
}
}
}
$this->dao->update(TABLE_CRON)->set('lastTime')->eq(date(DT_DATETIME1))->where('`lastTime` IS NULL')->andWhere('status')->ne('stop')->exec();
return $parsedCrons;
}
/**
* 修改定时任务状态。
* Change cron status.
*
* @param int $cronID
* @param string $status
* @param bool $changeTime
* @access public
* @return bool
*/
public function changeStatus(int $cronID, string $status, bool $changeTime = false): bool
{
$data = new stdclass();
$data->status = $status;
if($status == 'running' or $changeTime) $data->lastTime = date(DT_DATETIME1);
$this->dao->update(TABLE_CRON)->data($data)->where('id')->eq($cronID)->exec();
return dao::isError() ? false : true;
}
/**
* 记录定时任务日志。
* Log cron.
*
* @param string $log
* @param bool $isDebugMode
* @access public
* @return void
*/
public function logCron(string $log, bool $isDebugMode = false)
{
if($isDebugMode && !$this->config->debug) return false;
if(!is_writable($this->app->getLogRoot())) return false;
$runMode = PHP_SAPI == 'cli' ? '_cli' : '';
$file = $this->app->getLogRoot() . "cron$runMode." . date('Ymd') . '.log.php';
if(!is_file($file)) $log = "<?php\n die();\n" . $log;
$fp = fopen($file, "a");
fwrite($fp, $log);
fclose($fp);
}
/**
* 获取定时任务最后执行时间。
* Get last executed time.
*
* @access public
* @return string
*/
public function getLastTime(): string
{
return zget(zget($this->config->cron, 'scheduler', array()), 'lastTime', '');
}
/**
* 检查定时任务是否还在工作。
* Runnable cron.
*
* @access public
* @return bool
*/
public function runnable(): bool
{
if(empty($this->config->global->cron)) return false;
$lastTime = $this->getLastTime();
if(helper::isZeroDate($lastTime) or ((time() - strtotime($lastTime)) > $this->config->cron->maxRunTime)) return true;
return false;
}
/**
* 检查定时任务是否已修改。
* Check change cron.
*
* @access public
* @return bool
*/
public function checkChange(): bool
{
$updatedCron = $this->dao->select('*')->from(TABLE_CRON)->where('`lastTime` IS NULL')->andWhere('status')->ne('stop')->fetch();
return $updatedCron ? true : false;
}
/**
* 创建定时任务。
* Create cron.
*
* @access public
* @return int
*/
public function create(): int
{
$cron = fixer::input('post')
->add('status', 'normal')
->skipSpecial('m,h,dom,mon,dow,command')
->get();
if(!$this->config->features->cronSystemCall and $cron->type == 'system')
{
dao::$errors[] = $this->lang->cron->notice->errorType;
return false;
}
$result = $this->checkRule($cron);
if(!empty($result))
{
dao::$errors = $result;
return false;
}
$this->dao->insert(TABLE_CRON)->data($cron)->autoCheck()->exec();
return $this->dao->lastInsertID();
}
/**
* 修改定时任务。
* Update cron.
*
* @param int $cronID
* @access public
* @return bool
*/
public function update(int $cronID): bool
{
$cron = fixer::input('post')
->add('lastTime', NULL)
->skipSpecial('m,h,dom,mon,dow,command')
->get();
if(!$this->config->features->cronSystemCall and $cron->type == 'system')
{
dao::$errors[] = $this->lang->cron->notice->errorType;
return false;
}
$result = $this->checkRule($cron);
if(!empty($result))
{
dao::$errors = $result;
return false;
}
$this->dao->update(TABLE_CRON)->data($cron)->autoCheck()->where('id')->eq($cronID)->exec();
return dao::isError() ? false : true;
}
/**
* 检查定时任务是否符合规则。
* Check cron rule.
*
* @param object $cron
* @access public
* @return array
*/
public function checkRule(object $cron): array
{
if($cron->m === '' || $cron->m[0] == '-' || preg_match('/[^0-9\*\-\/,]/', $cron->m)) return array('m' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->m));
if($cron->h === '' || $cron->h[0] == '-' || preg_match('/[^0-9\*\-\/,]/', $cron->h)) return array('h' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->h));
if($cron->dom === '' || $cron->dom[0] == '-' || preg_match('/[^0-9\*\-\/,\?LWC]/', $cron->dom))return array('dom' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dom));
if($cron->mon === '' || $cron->mon[0] == '-' || preg_match('/[^0-9\*\-\/,]/', $cron->mon)) return array('mon' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->mon));
if($cron->dow === '' || $cron->dow[0] == '-' || preg_match('/[^0-9\*\-\/,\?LC#]/', $cron->dow))return array('dow' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dow));
if(is_numeric($cron->m) && ($cron->m < 0 || $cron->m > 59)) return array('m' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->m));
if(is_numeric($cron->h) && ($cron->h < 0 || $cron->h > 23)) return array('h' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->h));
if(is_numeric($cron->dom) && ($cron->dom < 1 || $cron->dom > 31)) return array('dom' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dom));
if(is_numeric($cron->mon) && ($cron->mon < 1 || $cron->mon > 12)) return array('mon' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->mon));
if(is_numeric($cron->dow) && ($cron->dow < 0 || $cron->dow > 6)) return array('dow' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dow));
if(empty($cron->command)) return array('command' => sprintf($this->lang->error->notempty, $this->lang->cron->command));
return array();
}
/**
* 重启cron更新scheduler的execId。
* Restart cron.
*
* @access public
* @return void
*/
public function restartCron($execId)
{
$this->dao->update(TABLE_CONFIG)->set('value')->eq($execId)
->where('owner')->eq('system')
->andWhere('module')->eq('cron')
->andWhere('section')->eq('scheduler')
->andWhere('`key`')->eq('execId')
->exec();
$this->dao->delete()->from(TABLE_QUEUE)->where('createdDate')->le(date("Y-m-d H:i:s", strtotime("-1 week")))->exec();
$this->logCron(date('G:i:s') . " restart\n\n");
}
/**
* 更新定时任务的最后执行时间。
* Update last time of cron.
*
* @param string $role
* @param int $execId
* @access public
* @return void
*/
public function updateTime(string $role, int $execId)
{
$now = date(DT_DATETIME1);
$settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->andWhere('section')->eq($role)->fetchAll('key', false);
if($role == 'scheduler')
{
if(isset($settings['execId']))
{
$setting = $settings['execId'];
if($setting->value != strval($execId)) $this->dao->update(TABLE_CONFIG)->set('value')->eq($execId)->where('id')->eq($setting->id)->exec();
}
else
{
$data = new stdclass();
$data->owner = 'system';
$data->module = 'cron';
$data->section = 'scheduler';
$data->key = 'execId';
$data->value = $execId;
$this->dao->insert(TABLE_CONFIG)->data($data)->exec();
}
if(isset($settings['lastTime']))
{
$setting = $settings['lastTime'];
$this->dao->update(TABLE_CONFIG)->set('value')->eq($now)->where('id')->eq($setting->id)->exec();
}
else
{
$data = new stdclass();
$data->owner = 'system';
$data->module = 'cron';
$data->section = 'scheduler';
$data->key = 'lastTime';
$data->value = $now;
$this->dao->insert(TABLE_CONFIG)->data($data)->exec();
}
}
else
{
if(isset($settings[strval($execId)]))
{
$setting = $settings[strval($execId)];
$this->dao->update(TABLE_CONFIG)->set('value')->eq($now)->where('id')->eq($setting->id)->exec();
}
else
{
$data = new stdclass();
$data->owner = 'system';
$data->module = 'cron';
$data->section = 'consumer';
$data->key = $execId;
$data->value = $now;
$this->dao->insert(TABLE_CONFIG)->data($data)->exec();
}
}
}
}