File: /var/www/vhost/disk-apps/magento.bikenow.co/vendor/magento/module-cron/Model/Schedule.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
{
const STATUS_PENDING = 'pending';
const STATUS_RUNNING = 'running';
const STATUS_SUCCESS = 'success';
const STATUS_MISSED = 'missed';
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 = preg_split('#\s+#', $expr, null, 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(null, 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 (strpos($expr, ',') !== false) {
foreach (explode(',', $expr) as $e) {
if ($this->matchCronExpression($e, $num)) {
return true;
}
}
return false;
}
// handle modulus
if (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 (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) {
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;
}
}