model.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <?php
  2. /**
  3. * The model file of cron module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yidong Wang <yidong@cnezsoft.com>
  8. * @package cron
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class cronModel extends model
  13. {
  14. /**
  15. * 通过ID获取定时任务。
  16. * Get by Id.
  17. *
  18. * @param int $cronID
  19. * @access public
  20. * @return object
  21. */
  22. public function getById($cronID)
  23. {
  24. return $this->dao->select('*')->from(TABLE_CRON)->where('id')->eq($cronID)->fetch();
  25. }
  26. /**
  27. * Get crons.
  28. *
  29. * @param string $params
  30. * @access public
  31. * @return array
  32. */
  33. public function getCrons($params = '')
  34. {
  35. return $this->dao->select('*')->from(TABLE_CRON)
  36. ->where('1=1')
  37. ->beginIF(strpos($params, 'nostop') !== false)->andWhere('status')->ne('stop')->fi()
  38. ->beginIF($this->config->edition != 'max')
  39. ->andWhere('command')->ne('moduleName=measurement&methodName=initCrontabQueue')
  40. ->andWhere('command')->ne('moduleName=measurement&methodName=execCrontabQueue')
  41. ->fi()
  42. ->fetchAll('id', false);
  43. }
  44. /**
  45. * Parse crons.
  46. *
  47. * @param array $crons
  48. * @access public
  49. * @return array
  50. */
  51. public function parseCron($crons)
  52. {
  53. $this->app->loadClass('crontab', true);
  54. $parsedCrons = array();
  55. foreach($crons as $cron)
  56. {
  57. $row = "{$cron->m} {$cron->h} {$cron->dom} {$cron->mon} {$cron->dow} {$cron->command}";
  58. preg_match_all('/(\S+\s+){5}|.*/', $row, $matches);
  59. if($matches[0])
  60. {
  61. try
  62. {
  63. $runTime = isset($cron->datetime) ? $cron->datetime : date(DT_DATETIME1);
  64. $parsedCron = array();
  65. $parsedCron['schema'] = trim($matches[0][0]);
  66. $parsedCron['command'] = trim($matches[0][1]);
  67. $parsedCron['cron'] = crontab::factory($parsedCron['schema']);
  68. $parsedCron['time'] = $parsedCron['cron']->getNextRunDate($runTime);
  69. $parsedCrons[$cron->id] = $parsedCron;
  70. }
  71. catch(InvalidArgumentException $e)
  72. {
  73. $this->dao->update(TABLE_CRON)->set('status')->eq('stop')->where('id')->eq($cron->id)->exec();
  74. continue;
  75. }
  76. }
  77. }
  78. $this->dao->update(TABLE_CRON)->set('lastTime')->eq(date(DT_DATETIME1))->where('`lastTime` IS NULL')->andWhere('status')->ne('stop')->exec();
  79. return $parsedCrons;
  80. }
  81. /**
  82. * 修改定时任务状态。
  83. * Change cron status.
  84. *
  85. * @param int $cronID
  86. * @param string $status
  87. * @param bool $changeTime
  88. * @access public
  89. * @return bool
  90. */
  91. public function changeStatus($cronID, $status, $changeTime = false)
  92. {
  93. $data = new stdclass();
  94. $data->status = $status;
  95. if($status == 'running' or $changeTime) $data->lastTime = date(DT_DATETIME1);
  96. $this->dao->update(TABLE_CRON)->data($data)->where('id')->eq($cronID)->exec();
  97. return dao::isError() ? false : true;
  98. }
  99. /**
  100. * 记录定时任务日志。
  101. * Log cron.
  102. *
  103. * @param string $log
  104. * @param bool $isDebugMode
  105. * @access public
  106. * @return void
  107. */
  108. public function logCron($log, $isDebugMode = false)
  109. {
  110. if($isDebugMode && !$this->config->debug) return false;
  111. if(!is_writable($this->app->getLogRoot())) return false;
  112. $runMode = PHP_SAPI == 'cli' ? '_cli' : '';
  113. $file = $this->app->getLogRoot() . "cron$runMode." . date('Ymd') . '.log.php';
  114. if(!is_file($file)) $log = "<?php\n die();\n" . $log;
  115. $fp = fopen($file, "a");
  116. fwrite($fp, $log);
  117. fclose($fp);
  118. }
  119. /**
  120. * 获取定时任务最后执行时间。
  121. * Get last executed time.
  122. *
  123. * @access public
  124. * @return string
  125. */
  126. public function getLastTime()
  127. {
  128. return zget(zget($this->config->cron, 'scheduler', array()), 'lastTime', '');
  129. }
  130. /**
  131. * 检查定时任务是否还在工作。
  132. * Runnable cron.
  133. *
  134. * @access public
  135. * @return bool
  136. */
  137. public function runnable()
  138. {
  139. if(empty($this->config->global->cron)) return false;
  140. $lastTime = $this->getLastTime();
  141. if(helper::isZeroDate($lastTime) or ((time() - strtotime($lastTime)) > $this->config->cron->maxRunTime)) return true;
  142. return false;
  143. }
  144. /**
  145. * 检查定时任务是否已修改。
  146. * Check change cron.
  147. *
  148. * @access public
  149. * @return bool
  150. */
  151. public function checkChange()
  152. {
  153. $updatedCron = $this->dao->select('*')->from(TABLE_CRON)->where('`lastTime` IS NULL')->andWhere('status')->ne('stop')->fetch();
  154. return $updatedCron ? true : false;
  155. }
  156. /**
  157. * 创建定时任务。
  158. * Create cron.
  159. *
  160. * @access public
  161. * @return int
  162. */
  163. public function create()
  164. {
  165. $cron = fixer::input('post')
  166. ->add('status', 'normal')
  167. ->skipSpecial('m,h,dom,mon,dow,command')
  168. ->get();
  169. if(!$this->config->features->cronSystemCall and $cron->type == 'system')
  170. {
  171. dao::$errors[] = $this->lang->cron->notice->errorType;
  172. return false;
  173. }
  174. $result = $this->checkRule($cron);
  175. if(!empty($result))
  176. {
  177. dao::$errors = $result;
  178. return false;
  179. }
  180. $this->dao->insert(TABLE_CRON)->data($cron)->autoCheck()->exec();
  181. return $this->dao->lastInsertID();
  182. }
  183. /**
  184. * 修改定时任务。
  185. * Update cron.
  186. *
  187. * @param int $cronID
  188. * @access public
  189. * @return bool
  190. */
  191. public function update($cronID)
  192. {
  193. $cron = fixer::input('post')
  194. ->add('lastTime', NULL)
  195. ->skipSpecial('m,h,dom,mon,dow,command')
  196. ->get();
  197. if(!$this->config->features->cronSystemCall and $cron->type == 'system')
  198. {
  199. dao::$errors[] = $this->lang->cron->notice->errorType;
  200. return false;
  201. }
  202. $result = $this->checkRule($cron);
  203. if(!empty($result))
  204. {
  205. dao::$errors = $result;
  206. return false;
  207. }
  208. $this->dao->update(TABLE_CRON)->data($cron)->autoCheck()->where('id')->eq($cronID)->exec();
  209. return dao::isError() ? false : true;
  210. }
  211. /**
  212. * 检查定时任务是否符合规则。
  213. * Check cron rule.
  214. *
  215. * @param object $cron
  216. * @access public
  217. * @return array
  218. */
  219. public function checkRule($cron)
  220. {
  221. 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));
  222. 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));
  223. 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));
  224. 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));
  225. 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));
  226. if(is_numeric($cron->m) && ($cron->m < 0 || $cron->m > 59)) return array('m' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->m));
  227. if(is_numeric($cron->h) && ($cron->h < 0 || $cron->h > 23)) return array('h' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->h));
  228. if(is_numeric($cron->dom) && ($cron->dom < 1 || $cron->dom > 31)) return array('dom' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dom));
  229. if(is_numeric($cron->mon) && ($cron->mon < 1 || $cron->mon > 12)) return array('mon' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->mon));
  230. if(is_numeric($cron->dow) && ($cron->dow < 0 || $cron->dow > 6)) return array('dow' => sprintf($this->lang->cron->notice->errorRule, $this->lang->cron->dow));
  231. if(empty($cron->command)) return array('command' => sprintf($this->lang->error->notempty, $this->lang->cron->command));
  232. return array();
  233. }
  234. /**
  235. * 重启cron,更新scheduler的execId。
  236. * Restart cron.
  237. *
  238. * @access public
  239. * @return void
  240. */
  241. public function restartCron($execId)
  242. {
  243. $this->dao->update(TABLE_CONFIG)->set('value')->eq($execId)
  244. ->where('owner')->eq('system')
  245. ->andWhere('module')->eq('cron')
  246. ->andWhere('section')->eq('scheduler')
  247. ->andWhere('`key`')->eq('execId')
  248. ->exec();
  249. $this->dao->delete()->from(TABLE_QUEUE)->where('createdDate')->le(date("Y-m-d H:i:s", strtotime("-1 week")))->exec();
  250. $this->logCron(date('G:i:s') . " restart\n\n");
  251. }
  252. /**
  253. * 更新定时任务的最后执行时间。
  254. * Update last time of cron.
  255. *
  256. * @param string $role
  257. * @param int $execId
  258. * @access public
  259. * @return void
  260. */
  261. public function updateTime($role, $execId)
  262. {
  263. $now = date(DT_DATETIME1);
  264. $settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->andWhere('section')->eq($role)->fetchAll('key', false);
  265. if($role == 'scheduler')
  266. {
  267. if(isset($settings['execId']))
  268. {
  269. $setting = $settings['execId'];
  270. if($setting->value != strval($execId)) $this->dao->update(TABLE_CONFIG)->set('value')->eq($execId)->where('id')->eq($setting->id)->exec();
  271. }
  272. else
  273. {
  274. $data = new stdclass();
  275. $data->owner = 'system';
  276. $data->module = 'cron';
  277. $data->section = 'scheduler';
  278. $data->key = 'execId';
  279. $data->value = $execId;
  280. $this->dao->insert(TABLE_CONFIG)->data($data)->exec();
  281. }
  282. if(isset($settings['lastTime']))
  283. {
  284. $setting = $settings['lastTime'];
  285. $this->dao->update(TABLE_CONFIG)->set('value')->eq($now)->where('id')->eq($setting->id)->exec();
  286. }
  287. else
  288. {
  289. $data = new stdclass();
  290. $data->owner = 'system';
  291. $data->module = 'cron';
  292. $data->section = 'scheduler';
  293. $data->key = 'lastTime';
  294. $data->value = $now;
  295. $this->dao->insert(TABLE_CONFIG)->data($data)->exec();
  296. }
  297. }
  298. else
  299. {
  300. if(isset($settings[strval($execId)]))
  301. {
  302. $setting = $settings[strval($execId)];
  303. $this->dao->update(TABLE_CONFIG)->set('value')->eq($now)->where('id')->eq($setting->id)->exec();
  304. }
  305. else
  306. {
  307. $data = new stdclass();
  308. $data->owner = 'system';
  309. $data->module = 'cron';
  310. $data->section = 'consumer';
  311. $data->key = $execId;
  312. $data->value = $now;
  313. $this->dao->insert(TABLE_CONFIG)->data($data)->exec();
  314. }
  315. }
  316. }
  317. }