<?php
/**
* Copyright (c) 2011-present Qualiteam software Ltd. All rights reserved.
* See https://www.x-cart.com/license-agreement.html for license details.
*/
namespace XLite\Core\Task;
use Symfony\Component\Console\Output\OutputInterface;
use XCart\Event\Task\CronEvent;
/**
* Abstract task
*/
abstract class ATask extends \XLite\Base
{
/**
* Model
*
* @var \XLite\Model\Task
*/
protected $model;
/**
* Start time
*
* @var integer
*/
protected $startTime;
/**
* Last step flag
*
* @var boolean
*/
protected $lastStep = false;
/**
* Result operation message
*
* @var string
*/
protected $message;
/**
* @var OutputInterface
*/
protected $output;
/**
* Get title
*
* @return string
*/
abstract public function getTitle();
/**
* Run step
*/
abstract protected function runStep();
/**
* Constructor
*/
public function __construct()
{
}
public function handleTask(CronEvent $event): void
{
$this->model = \XLite\Core\Database::getRepo('XLite\Model\Task')->findOneBy(['owner' => static::class]);
$this->startTime = $event->getStartTime();
$this->output = $event->getOutput();
if (!$this->model->isExpired()) {
return;
}
$this->runRunner();
sleep($event->getSleepTime());
if (!$this->checkThreadResource($event)) {
$time = gmdate('H:i:s', \XLite\Core\Converter::time() - $this->startTime);
$memory = \XLite\Core\Converter::formatFileSize(memory_get_usage(true));
$this->printContent('Step is interrupted (time: ' . $time . '; memory usage: ' . $memory . ')');
$event->stopPropagation();
}
}
/**
* Get message
*
* @return string
*/
public function getMessage()
{
return $this->message;
}
/**
* Check - task ready or not
*
* @return boolean
*/
public function isReady()
{
return true;
}
/**
* Should task started if previous attempt has failed
*
* @return boolean
*/
public function shouldRunIfCrashed()
{
return true;
}
/**
* Lock key
*
* @return string
*/
public function getLockKey()
{
return static::class . $this->model->getId();
}
/**
* Check - task ready or not
*
* @return boolean
*/
public function isRunning()
{
return \XLite\Core\Lock\FileLock::getInstance()->isRunning(
$this->getLockKey(),
!$this->shouldRunIfCrashed()
);
}
/**
* Mark task as running
*
* @return void
*/
protected function markAsRunning()
{
\XLite\Core\Lock\FileLock::getInstance()->setRunning(
$this->getLockKey()
);
}
/**
* mark as not running
*
* @return void
*/
protected function release()
{
\XLite\Core\Lock\FileLock::getInstance()->release(
$this->getLockKey()
);
}
/**
* @return void
*/
protected function runRunner()
{
$silence = !$this->getTitle();
if ($this->isReady() && !$this->isRunning()) {
if (!$silence) {
$this->printContent($this->getTitle() . ' ... ');
}
$this->run();
if (!$silence) {
$this->printContent($this->getMessage() ?: 'done');
}
} elseif ($this->isRunning()) {
$msg = !$this->shouldRunIfCrashed()
? '| Task will not be restarted because previous attempt has failed. Remove lock files manually to start the task'
: '';
$this->printContent($this->getTitle() . ' ... Already running ' . $msg);
}
if (!$silence) {
$this->printContent(PHP_EOL);
}
\XLite\Core\Database::getEM()->flush();
}
/**
* Run task
*/
public function run()
{
if ($this->isValid()) {
$this->prepareStep();
$this->markAsRunning();
$this->runStep();
if ($this->isLastStep()) {
$this->finalizeTask();
} else {
$this->finalizeStep();
}
} elseif (!$this->message) {
$this->message = 'invalid';
}
}
/**
* Check thread resource
*
* @return boolean
*/
protected function checkThreadResource($event)
{
return time() - $event->getStartTime() < $event->getTimeLimit()
&& $event->getMemoryLimitIni() - memory_get_usage(true) > $event->getMemoryLimit();
}
/**
* Print content
*
* @param string $str Content
*
* @return void
*/
protected function printContent($str)
{
if (PHP_SAPI === 'cli') {
$this->output->write($str);
}
}
/**
* Prepare step
*
* @return void
*/
protected function prepareStep()
{
}
/**
* Check - current step is last or not
*
* @return boolean
*/
protected function isLastStep()
{
return $this->lastStep;
}
/**
* Finalize task (last step)
*/
protected function finalizeTask()
{
$this->release();
$this->close();
}
/**
* Finalize step
*/
protected function finalizeStep()
{
}
/**
* Check availability
*
* @return boolean
*/
protected function isValid()
{
return true;
}
/**
* Close task
*/
protected function close()
{
\XLite\Core\Database::getEM()->remove($this->model);
}
}