Source of file Cron.php
Size: 16,781 Bytes - Last Modified: 2020-10-24T02:46:31+00:00
/home/travis/build/NextDom/nextdom-core/src/Model/Entity/Cron.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 | <?php /* This file is part of NextDom Software. * * NextDom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NextDom Software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NextDom Software. If not, see <http://www.gnu.org/licenses/>. */ namespace NextDom\Model\Entity; use NextDom\Enums\DateFormat; use NextDom\Enums\LogTarget; use NextDom\Enums\NextDomObj; use NextDom\Exceptions\CoreException; use NextDom\Helpers\DBHelper; use NextDom\Helpers\LogHelper; use NextDom\Helpers\SystemHelper; use NextDom\Helpers\Utils; use NextDom\Managers\CacheManager; use NextDom\Managers\ConfigManager; use NextDom\Managers\CronManager; use NextDom\Model\Entity\Parents\BaseEntity; use NextDom\Model\Entity\Parents\EnableEntity; /** * Cron * * @ORM\Table(name="cron", uniqueConstraints={@ORM\UniqueConstraint(name="class_function_option", columns={"class", "function", "option"})}, indexes={@ORM\Index(name="type", columns={"class"}), @ORM\Index(name="logicalId_Type", columns={"class"}), @ORM\Index(name="deamon", columns={"deamon"})}) * @ORM\Entity */ class Cron extends BaseEntity { const TABLE_NAME = NextDomObj::CRON; use EnableEntity; /** * @var string * * @ORM\Column(name="class", type="string", length=127, nullable=true) */ protected $class = ''; /** * @var string * * @ORM\Column(name="function", type="string", length=127, nullable=false) */ protected $function; /** * @var string * * @ORM\Column(name="schedule", type="string", length=127, nullable=true) */ protected $schedule = ''; /** * @var integer * * @ORM\Column(name="timeout", type="integer", nullable=true) */ protected $timeout; /** * @var integer * * @ORM\Column(name="deamon", type="integer", nullable=true) */ protected $deamon = 0; /** * @var integer * * @ORM\Column(name="deamonSleepTime", type="integer", nullable=true) */ protected $deamonSleepTime; /** * @var string * * @ORM\Column(name="option", type="string", length=255, nullable=true) */ protected $option; /** * @var integer * * @ORM\Column(name="once", type="integer", nullable=true) */ protected $once = 0; /** * Get enabled state of the cron task * * @param int $defaultValue Default value if cron task is not initialized * * @return int 1 for enabled task, 0 for disabled, or defaultValue */ public function getEnable($defaultValue = 0) { if ($this->enable == '' || !is_numeric($this->enable)) { return $defaultValue; } return $this->enable; } /** * Get timeout of the task * If timeout is not configured, return default value from * * @return int Timeout * * @throws \Exception */ public function getTimeout() { $timeout = $this->timeout; if ($timeout == 0) { $timeout = ConfigManager::byKey('maxExecTimeCrontask'); } return $timeout; } /** * * @param $_timeout * @return $this */ public function setTimeout($_timeout) { $this->updateChangeState($this->timeout, $_timeout); $this->timeout = $_timeout; return $this; } /** * @return int */ public function getDeamon() { return $this->deamon; } /** * * @param $_deamons * @return $this */ public function setDeamon($_deamons) { $this->updateChangeState($this->deamon, $_deamons); $this->deamon = $_deamons; return $this; } /** * @return int|mixed * @throws \Exception */ public function getDeamonSleepTime() { $deamonSleepTime = $this->deamonSleepTime; if ($deamonSleepTime == 0) { $deamonSleepTime = ConfigManager::byKey('deamonsSleepTime'); } return $deamonSleepTime; } /** * * @param $_deamonSleepTime * @return $this */ public function setDeamonSleepTime($_deamonSleepTime) { $this->updateChangeState($this->deamonSleepTime, $_deamonSleepTime); $this->deamonSleepTime = $_deamonSleepTime; return $this; } /** * @param int $defaultValue * @return int */ public function getOnce($defaultValue = 0) { if ($this->once == '' || !is_numeric($this->once)) { return $defaultValue; } return $this->once; } /** * * @param $_once * @return $this */ public function setOnce($_once) { $this->updateChangeState($this->once, $_once); $this->once = $_once; return $this; } /** * Check if cron object is valid before save * @throws CoreException */ public function preSave() { if ($this->getFunction() == '') { throw new CoreException(__('La fonction ne peut pas être vide')); } if ($this->getSchedule() == '') { throw new CoreException(__('La programmation ne peut pas être vide : ') . print_r($this, true)); } if ($this->getOption() == '' || count($this->getOption()) == 0) { $cron = CronManager::byClassAndFunction($this->getClass(), $this->getFunction()); if (is_object($cron)) { $this->setId($cron->getId()); } } } /** * @return string */ public function getFunction() { return $this->function; } /** * * @param $_function * @return $this */ public function setFunction($_function) { $this->updateChangeState($this->function, $_function); $this->function = $_function; return $this; } /** * @return string */ public function getSchedule() { return $this->schedule; } /** * * @param $_schedule * @return $this */ public function setSchedule($_schedule) { $this->updateChangeState($this->schedule, $_schedule); $this->schedule = $_schedule; return $this; } /** * @return mixed */ public function getOption() { return json_decode($this->option, true); } /** * * @param $_option * @return $this */ public function setOption($_option) { $_option = json_encode($_option, JSON_UNESCAPED_UNICODE); $this->updateChangeState($this->option, $_option); $this->option = $_option; return $this; } /** * @return string */ public function getClass() { return $this->class; } /** * * @param $newClass * @return $this */ public function setClass($newClass) { $this->updateChangeState($this->class, $newClass); $this->class = $newClass; return $this; } /** * Stop task after insert in database */ public function postInsert() { $this->setState('stop'); $this->setPID(); } /** * Set task state * * @param mixed $state State of the task * @throws \Exception */ public function setState($state) { $this->setCache('state', $state); } /** * Store task data in cache * * @param mixed $cacheKey * @param mixed $cacheValue * @throws \Exception */ public function setCache($cacheKey, $cacheValue = null) { CacheManager::set('cronCacheAttr' . $this->getId(), Utils::setJsonAttr(CacheManager::byKey('cronCacheAttr' . $this->getId())->getValue(), $cacheKey, $cacheValue)); } /** * Store PID in cache * * @param mixed $pid * @throws \Exception */ public function setPID($pid = null) { $this->setCache('pid', $pid); } /** * Save cron object in database * * @return mixed * @throws CoreException * @throws \ReflectionException */ public function save() { return DBHelper::save($this, false, true); } /** * Remove cron object from the database * * @param bool $haltBefore * @return mixed * @throws CoreException * @throws \ReflectionException */ public function remove($haltBefore = true) { if ($haltBefore && $this->running()) { $this->halt(); } CacheManager::delete('cronCacheAttr' . $this->getId()); return parent::remove(); } /** * Check if this cron is currently running * * @return boolean * @throws \Exception */ public function running(): bool { if (($this->getState() == 'run' || $this->getState() == 'stoping') && $this->getPID() > 0) { if (posix_getsid($this->getPID()) && (!file_exists('/proc/' . $this->getPID() . '/cmdline') || strpos(@file_get_contents('/proc/' . $this->getPID() . '/cmdline'), 'cron_id=' . $this->getId()) !== false)) { return true; } } if (count(SystemHelper::ps('cron_id=' . $this->getId() . '$')) > 0) { return true; } return false; } /** * Get current state * * @return mixed Current state * @throws \Exception */ public function getState() { return $this->getCache('state', 'stop'); } /** * Get task data in cache * * @param string $cacheKey * @param string $cacheValue * @return mixed * @throws \Exception */ public function getCache($cacheKey = '', $cacheValue = '') { $cache = CacheManager::byKey('cronCacheAttr' . $this->getId())->getValue(); return Utils::getJsonAttr($cache, $cacheKey, $cacheValue); } /** * Get task PID * * @param mixed $defaultValue * * @return mixed Task PID * @throws \Exception */ public function getPID($defaultValue = null) { return $this->getCache('pid', $defaultValue); } /** * Stop immediatly cron (this method must be only call by jeecron master) */ public function halt() { if (!$this->running()) { $this->setState('stop'); $this->setPID(); } else { LogHelper::addInfo(LogTarget::CRON, __('Arrêt de ') . $this->getClass() . '::' . $this->getFunction() . '(), PID : ' . $this->getPID()); if ($this->getPID() > 0) { SystemHelper::kill($this->getPID()); $retry = 0; while ($this->running() && $retry < (ConfigManager::byKey('deamonsSleepTime') + 5)) { sleep(1); SystemHelper::kill($this->getPID()); $retry++; } $retry = 0; while ($this->running() && $retry < (ConfigManager::byKey('deamonsSleepTime') + 5)) { sleep(1); SystemHelper::kill($this->getPID()); $retry++; } } if ($this->running()) { SystemHelper::kill("cron_id=" . $this->getId() . "$"); sleep(1); if ($this->running()) { SystemHelper::kill("cron_id=" . $this->getId() . "$"); sleep(1); } if ($this->running()) { $this->setState('error'); $this->setPID(); throw new CoreException($this->getClass() . '::' . $this->getFunction() . __('() : Impossible d\'arrêter la tâche')); } } else { $this->setState('stop'); $this->setPID(); } } return true; } /** * Start cron task */ public function start() { if (!$this->running()) { $this->setState('starting'); } else { $this->setState('run'); } } /** * Launch cron (this method must be only call by jeeCron master) * * @param bool $noErrorReport * @throws CoreException */ public function run($noErrorReport = false) { $cmd = NEXTDOM_ROOT . '/src/Api/start_cron.php'; $cmd .= ' "cron_id=' . $this->getId() . '"'; if (!$this->running()) { SystemHelper::php($cmd . ' >> ' . LogHelper::getPathToLog('cron_execution') . ' 2>&1 &'); } else { if (!$noErrorReport) { $this->halt(); if (!$this->running()) { exec($cmd . ' >> ' . LogHelper::getPathToLog('cron_execution') . ' 2>&1 &'); } else { throw new CoreException(__('Impossible d\'exécuter la tâche car elle est déjà en cours d\'exécution (') . ' : ' . $cmd); } } } } /** * Refresh DB state of this cron * * @return boolean * @throws \Exception */ public function refresh(): bool { if (($this->getState() == 'run' || $this->getState() == 'stoping') && !$this->running()) { $this->setState('stop'); $this->setPID(); } return true; } /** * Stop task */ public function stop() { if ($this->running()) { $this->setState('stoping'); } } /** * Check if it's time to launch cron * * @return boolean * @throws \Exception */ public function isDue(): bool { //check if already sent on that minute $last = strtotime($this->getLastRun()); $now = time(); $now = ($now - $now % 60); $last = ($last - $last % 60); if ($now == $last) { return false; } try { $c = new \Cron\CronExpression($this->getSchedule(), new \Cron\FieldFactory); try { if ($c->isDue()) { return true; } } catch (\Exception $e) { } try { $prev = $c->getPreviousRunDate()->getTimestamp(); } catch (\Exception $e) { return false; } $diff = abs((strtotime('now') - $prev) / 60); if (strtotime($this->getLastRun()) < $prev && ($diff <= ConfigManager::byKey('maxCatchAllow') || ConfigManager::byKey('maxCatchAllow') == -1)) { return true; } } catch (\Exception $e) { LogHelper::addDebug(LogTarget::CRON, 'Error on isDue : ' . $e->getMessage() . ', cron : ' . $this->getSchedule()); } return false; } /** * Get last task run * * @return mixed Last task run * @throws \Exception */ public function getLastRun() { return $this->getCache('lastRun'); } /** * Get date of the next task run * * @return bool|string */ public function getNextRunDate() { try { $cronExpression = new \Cron\CronExpression($this->getSchedule(), new \Cron\FieldFactory); return $cronExpression->getNextRunDate()->format(DateFormat::FULL); } catch (\Exception $e) { } return false; } /** * Get human name of cron * @return string */ public function getName() { if ($this->getClass() != '') { return $this->getClass() . '::' . $this->getFunction() . '()'; } return $this->getFunction() . '()'; } /** * Get cron data in array * * @return array Cron data * @throws \Exception */ public function toArray() { $return = Utils::o2a($this, true); $return['state'] = $this->getState(); $return['lastRun'] = $this->getLastRun(); $return['pid'] = $this->getPID(); $return['runtime'] = $this->getCache('runtime'); return $return; } /** * Set last task run * * @param mixed $lastRun Last task run * @throws \Exception */ public function setLastRun($lastRun) { $this->setCache('lastRun', $lastRun); } } |