mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-08 01:37:36 +00:00
312 lines
9.6 KiB
PHP
312 lines
9.6 KiB
PHP
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
|
|
namespace Magento\Cron\Model;
|
|
|
|
use Magento\Framework\Exception\CronException;
|
|
use Magento\Framework\App\ObjectManager;
|
|
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
|
|
use Magento\Framework\Intl\DateTimeFactory;
|
|
|
|
/**
|
|
* Crontab schedule model
|
|
*
|
|
* @method string getJobCode()
|
|
* @method \Magento\Cron\Model\Schedule setJobCode(string $value)
|
|
* @method string getStatus()
|
|
* @method \Magento\Cron\Model\Schedule setStatus(string $value)
|
|
* @method string getMessages()
|
|
* @method \Magento\Cron\Model\Schedule setMessages(string $value)
|
|
* @method string getCreatedAt()
|
|
* @method \Magento\Cron\Model\Schedule setCreatedAt(string $value)
|
|
* @method string getScheduledAt()
|
|
* @method \Magento\Cron\Model\Schedule setScheduledAt(string $value)
|
|
* @method string getExecutedAt()
|
|
* @method \Magento\Cron\Model\Schedule setExecutedAt(string $value)
|
|
* @method string getFinishedAt()
|
|
* @method \Magento\Cron\Model\Schedule setFinishedAt(string $value)
|
|
* @method array getCronExprArr()
|
|
* @method \Magento\Cron\Model\Schedule setCronExprArr(array $value)
|
|
*
|
|
* @api
|
|
* @since 100.0.2
|
|
*/
|
|
class Schedule extends \Magento\Framework\Model\AbstractModel
|
|
{
|
|
public const STATUS_PENDING = 'pending';
|
|
|
|
public const STATUS_RUNNING = 'running';
|
|
|
|
public const STATUS_SUCCESS = 'success';
|
|
|
|
public const STATUS_MISSED = 'missed';
|
|
|
|
public const STATUS_ERROR = 'error';
|
|
|
|
/**
|
|
* @var TimezoneInterface
|
|
*/
|
|
private $timezoneConverter;
|
|
|
|
/**
|
|
* @var DateTimeFactory
|
|
*/
|
|
private $dateTimeFactory;
|
|
|
|
/**
|
|
* @var DeadlockRetrierInterface
|
|
*/
|
|
private $retrier;
|
|
|
|
/**
|
|
* @param \Magento\Framework\Model\Context $context
|
|
* @param \Magento\Framework\Registry $registry
|
|
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
|
|
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
|
|
* @param array $data
|
|
* @param TimezoneInterface|null $timezoneConverter
|
|
* @param DateTimeFactory|null $dateTimeFactory
|
|
* @param DeadlockRetrierInterface $retrier
|
|
*/
|
|
public function __construct(
|
|
\Magento\Framework\Model\Context $context,
|
|
\Magento\Framework\Registry $registry,
|
|
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
|
|
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
|
|
array $data = [],
|
|
TimezoneInterface $timezoneConverter = null,
|
|
DateTimeFactory $dateTimeFactory = null,
|
|
DeadlockRetrierInterface $retrier = null
|
|
) {
|
|
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
|
|
$this->timezoneConverter = $timezoneConverter ?: ObjectManager::getInstance()->get(TimezoneInterface::class);
|
|
$this->dateTimeFactory = $dateTimeFactory ?: ObjectManager::getInstance()->get(DateTimeFactory::class);
|
|
$this->retrier = $retrier ?: ObjectManager::getInstance()->get(DeadlockRetrierInterface::class);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function _construct()
|
|
{
|
|
$this->_init(\Magento\Cron\Model\ResourceModel\Schedule::class);
|
|
}
|
|
|
|
/**
|
|
* Set cron expression.
|
|
*
|
|
* @param string $expr
|
|
* @return $this
|
|
* @throws \Magento\Framework\Exception\CronException
|
|
*/
|
|
public function setCronExpr($expr)
|
|
{
|
|
$e = $expr !== null ? preg_split('#\s+#', $expr, -1, PREG_SPLIT_NO_EMPTY) : [];
|
|
if (count($e) < 5 || count($e) > 6) {
|
|
throw new CronException(__('Invalid cron expression: %1', $expr));
|
|
}
|
|
|
|
$this->setCronExprArr($e);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Checks the observer's cron expression against time.
|
|
*
|
|
* Supports $this->setCronExpr('* 0-5,10-59/5 2-10,15-25 january-june/2 mon-fri')
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function trySchedule()
|
|
{
|
|
$time = $this->getScheduledAt();
|
|
$e = $this->getCronExprArr();
|
|
|
|
if (!$e || !$time) {
|
|
return false;
|
|
}
|
|
$configTimeZone = $this->timezoneConverter->getConfigTimezone();
|
|
$storeDateTime = $this->dateTimeFactory->create('now', new \DateTimeZone($configTimeZone));
|
|
if (!is_numeric($time)) {
|
|
//convert time from UTC to admin store timezone
|
|
//we assume that all schedules in configuration (crontab.xml and DB tables) are in admin store timezone
|
|
$dateTimeUtc = $this->dateTimeFactory->create($time);
|
|
$time = $dateTimeUtc->getTimestamp();
|
|
}
|
|
$time = $storeDateTime->setTimestamp($time);
|
|
$match = $this->matchCronExpression($e[0], $time->format('i'))
|
|
&& $this->matchCronExpression($e[1], $time->format('H'))
|
|
&& $this->matchCronExpression($e[2], $time->format('d'))
|
|
&& $this->matchCronExpression($e[3], $time->format('m'))
|
|
&& $this->matchCronExpression($e[4], $time->format('w'));
|
|
|
|
return $match;
|
|
}
|
|
|
|
/**
|
|
* Match cron expression.
|
|
*
|
|
* @param string $expr
|
|
* @param int $num
|
|
* @return bool
|
|
* @throws \Magento\Framework\Exception\CronException
|
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
* @SuppressWarnings(PHPMD.NPathComplexity)
|
|
*/
|
|
public function matchCronExpression($expr, $num)
|
|
{
|
|
// handle ALL match
|
|
if ($expr === '*') {
|
|
return true;
|
|
}
|
|
|
|
// handle multiple options
|
|
if ($expr && strpos($expr, ',') !== false) {
|
|
foreach (explode(',', $expr) as $e) {
|
|
if ($this->matchCronExpression($e, $num)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// handle modulus
|
|
if ($expr && strpos($expr, '/') !== false) {
|
|
$e = explode('/', $expr);
|
|
if (count($e) !== 2) {
|
|
throw new CronException(__('Invalid cron expression, expecting \'match/modulus\': %1', $expr));
|
|
}
|
|
if (!is_numeric($e[1])) {
|
|
throw new CronException(__('Invalid cron expression, expecting numeric modulus: %1', $expr));
|
|
}
|
|
$expr = $e[0];
|
|
$mod = $e[1];
|
|
} else {
|
|
$mod = 1;
|
|
}
|
|
|
|
// handle all match by modulus
|
|
$offset = 0;
|
|
if ($expr === '*') {
|
|
$from = 0;
|
|
$to = 60;
|
|
} elseif ($expr && strpos($expr, '-') !== false) {
|
|
// handle range
|
|
$e = explode('-', $expr);
|
|
if (count($e) !== 2) {
|
|
throw new CronException(__('Invalid cron expression, expecting \'from-to\' structure: %1', $expr));
|
|
}
|
|
|
|
$from = $this->getNumeric($e[0]);
|
|
$to = $this->getNumeric($e[1]);
|
|
if ($mod !== 1) {
|
|
$offset = $from;
|
|
}
|
|
} elseif ($mod !== 1) {
|
|
$offset = $this->getNumeric($expr);
|
|
$from = 0;
|
|
$to = 60;
|
|
} else {
|
|
// handle regular token
|
|
$from = $this->getNumeric($expr);
|
|
$to = $from;
|
|
}
|
|
|
|
if ($from === false || $to === false || $mod == 0) {
|
|
throw new CronException(__('Invalid cron expression: %1', $expr));
|
|
}
|
|
|
|
return $num >= $from && $num <= $to && ($num - $offset) % $mod === 0;
|
|
}
|
|
|
|
/**
|
|
* Get number of a month.
|
|
*
|
|
* @param int|string $value
|
|
* @return bool|int|string
|
|
*/
|
|
public function getNumeric($value)
|
|
{
|
|
static $data = [
|
|
'jan' => 1,
|
|
'feb' => 2,
|
|
'mar' => 3,
|
|
'apr' => 4,
|
|
'may' => 5,
|
|
'jun' => 6,
|
|
'jul' => 7,
|
|
'aug' => 8,
|
|
'sep' => 9,
|
|
'oct' => 10,
|
|
'nov' => 11,
|
|
'dec' => 12,
|
|
'sun' => 0,
|
|
'mon' => 1,
|
|
'tue' => 2,
|
|
'wed' => 3,
|
|
'thu' => 4,
|
|
'fri' => 5,
|
|
'sat' => 6,
|
|
];
|
|
|
|
if (is_numeric($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (is_string($value)) {
|
|
$value = strtolower(substr($value, 0, 3));
|
|
if (isset($data[$value])) {
|
|
return $data[$value];
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sets a job to STATUS_RUNNING only if it is currently in STATUS_PENDING.
|
|
*
|
|
* Returns true if status was changed and false otherwise.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function tryLockJob()
|
|
{
|
|
/** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */
|
|
$scheduleResource = $this->_getResource();
|
|
|
|
// Change statuses from running to error for terminated jobs
|
|
$this->retrier->execute(
|
|
function () use ($scheduleResource) {
|
|
return $scheduleResource->getConnection()->update(
|
|
$scheduleResource->getTable('cron_schedule'),
|
|
['status' => self::STATUS_ERROR],
|
|
['job_code = ?' => $this->getJobCode(), 'status = ?' => self::STATUS_RUNNING]
|
|
);
|
|
},
|
|
$scheduleResource->getConnection()
|
|
);
|
|
|
|
// Change status from pending to running for ran jobs
|
|
$result = $this->retrier->execute(
|
|
function () use ($scheduleResource) {
|
|
return $scheduleResource->trySetJobStatusAtomic(
|
|
$this->getId(),
|
|
self::STATUS_RUNNING,
|
|
self::STATUS_PENDING
|
|
);
|
|
},
|
|
$scheduleResource->getConnection()
|
|
);
|
|
|
|
if ($result) {
|
|
$this->setStatus(self::STATUS_RUNNING);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|