classes/XLite/Core/Task/ATask.php line 82

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (c) 2001-present X-Cart Holdings LLC. All rights reserved.
  4.  * See https://www.x-cart.com/license-agreement.html for license details.
  5.  */
  6. namespace XLite\Core\Task;
  7. use Throwable;
  8. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  9. use Symfony\Component\Console\Output\OutputInterface;
  10. use XCart\Event\Task\CronEvent;
  11. use XCart\Event\Task\CronTaskEvent;
  12. use XCart\Event\Task\TaskDTO;
  13. use XCart\Container;
  14. /**
  15.  * Abstract task
  16.  */
  17. abstract class ATask extends \XLite\Base
  18. {
  19.     protected const STATUS_RUN 'Run';
  20.     protected const STATUS_RUNNING 'Already running';
  21.     protected const STATUS_ERROR 'Error';
  22.     protected const STATUS_INVALID 'Invalid';
  23.     /**
  24.      * Model
  25.      *
  26.      * @var \XLite\Model\Task
  27.      */
  28.     protected $model;
  29.     /**
  30.      * Start time
  31.      *
  32.      * @var integer
  33.      */
  34.     protected $startTime;
  35.     /**
  36.      * Last step flag
  37.      *
  38.      * @var boolean
  39.      */
  40.     protected $lastStep false;
  41.     /**
  42.      * Result operation message
  43.      *
  44.      * @var string
  45.      */
  46.     protected $message;
  47.     /**
  48.      * @var OutputInterface
  49.      */
  50.     protected $output;
  51.     protected string $cronEventId '';
  52.     /**
  53.      * Get title
  54.      *
  55.      * @return string
  56.      */
  57.     abstract public function getTitle();
  58.     /**
  59.      * Run step
  60.      */
  61.     abstract protected function runStep();
  62.     /**
  63.      * Constructor
  64.      */
  65.     public function __construct()
  66.     {
  67.     }
  68.     public function handleTask(CronEvent $event): void
  69.     {
  70.         $this->model \XLite\Core\Database::getRepo('XLite\Model\Task')->findOneBy(['owner' => static::class]);
  71.         $this->cronEventId $event->getId();
  72.         $this->startTime $event->getStartTime();
  73.         $this->output $event->getOutput();
  74.         if (!$this->model->isExpired()) {
  75.             return;
  76.         }
  77.         $this->runRunner();
  78.         sleep($event->getSleepTime());
  79.         if (!$this->checkThreadResource($event)) {
  80.             $time gmdate('H:i:s'\XLite\Core\Converter::time() - $this->startTime);
  81.             $memory \XLite\Core\Converter::formatFileSize(memory_get_usage(true));
  82.             $this->printContent('Step is interrupted (time: ' $time '; memory usage: ' $memory ')');
  83.             $event->stopPropagation();
  84.         }
  85.     }
  86.     /**
  87.      * Get message
  88.      *
  89.      * @return string
  90.      */
  91.     public function getMessage()
  92.     {
  93.         return $this->message;
  94.     }
  95.     /**
  96.      * Check - task ready or not
  97.      *
  98.      * @return boolean
  99.      */
  100.     public function isReady()
  101.     {
  102.         return true;
  103.     }
  104.     /**
  105.      * Should task started if previous attempt has failed
  106.      *
  107.      * @return boolean
  108.      */
  109.     public function shouldRunIfCrashed()
  110.     {
  111.         return true;
  112.     }
  113.     /**
  114.      * Lock key
  115.      *
  116.      * @return string
  117.      */
  118.     public function getLockKey()
  119.     {
  120.         return static::class . $this->model->getId();
  121.     }
  122.     /**
  123.      * Check - task ready or not
  124.      *
  125.      * @return boolean
  126.      */
  127.     public function isRunning()
  128.     {
  129.         return \XLite\Core\Lock\FileLock::getInstance()->isRunning(
  130.             $this->getLockKey(),
  131.             !$this->shouldRunIfCrashed()
  132.         );
  133.     }
  134.     /**
  135.      * Mark task as running
  136.      *
  137.      * @return void
  138.      */
  139.     protected function markAsRunning()
  140.     {
  141.         \XLite\Core\Lock\FileLock::getInstance()->setRunning(
  142.             $this->getLockKey()
  143.         );
  144.     }
  145.     /**
  146.      * mark as not running
  147.      *
  148.      * @return void
  149.      */
  150.     protected function release()
  151.     {
  152.         \XLite\Core\Lock\FileLock::getInstance()->release(
  153.             $this->getLockKey()
  154.         );
  155.     }
  156.     /**
  157.      * @return void
  158.      */
  159.     protected function runRunner()
  160.     {
  161.         $silence = !$this->getTitle();
  162.         if ($this->isReady() && !$this->isRunning()) {
  163.             if (!$silence) {
  164.                 $this->printContent($this->getTitle() . ' ... ');
  165.             }
  166.             try {
  167.                 $this->run();
  168.             } catch (Throwable $e) {
  169.                 $this->message $e->getMessage();
  170.                 $this->dispatchEvent(static::STATUS_ERROR$this->message);
  171.             }
  172.             if (!$silence) {
  173.                 $this->printContent($this->getMessage() ?: 'done');
  174.             }
  175.         } elseif ($this->isRunning()) {
  176.             $this->dispatchEvent(static::STATUS_RUNNING);
  177.             $msg = !$this->shouldRunIfCrashed()
  178.                 ? '| Task will not be restarted because previous attempt has failed. Remove lock files manually to start the task'
  179.                 '';
  180.             $this->printContent($this->getTitle() . ' ... Already running ' $msg);
  181.         }
  182.         if (!$silence) {
  183.             $this->printContent(PHP_EOL);
  184.         }
  185.         \XLite\Core\Database::getEM()->flush();
  186.     }
  187.     /**
  188.      * Run task
  189.      */
  190.     public function run()
  191.     {
  192.         if ($this->isValid()) {
  193.             $this->dispatchEvent(static::STATUS_RUN);
  194.             $this->prepareStep();
  195.             $this->markAsRunning();
  196.             $this->runStep();
  197.             if ($this->isLastStep()) {
  198.                 $this->finalizeTask();
  199.             } else {
  200.                 $this->finalizeStep();
  201.             }
  202.         } elseif (!$this->message) {
  203.             $this->dispatchEvent(static::STATUS_INVALID);
  204.             $this->message 'invalid';
  205.         }
  206.     }
  207.     /**
  208.      * Check thread resource
  209.      *
  210.      * @return boolean
  211.      */
  212.     protected function checkThreadResource($event)
  213.     {
  214.         return time() - $event->getStartTime() < $event->getTimeLimit()
  215.             && $event->getMemoryLimitIni() - memory_get_usage(true) > $event->getMemoryLimit();
  216.     }
  217.     /**
  218.      * Print content
  219.      *
  220.      * @param string $str Content
  221.      *
  222.      * @return void
  223.      */
  224.     protected function printContent($str)
  225.     {
  226.         if (PHP_SAPI === 'cli') {
  227.             $this->output->write($str);
  228.         }
  229.     }
  230.     /**
  231.      * Prepare step
  232.      *
  233.      * @return void
  234.      */
  235.     protected function prepareStep()
  236.     {
  237.     }
  238.     /**
  239.      * Check - current step is last or not
  240.      *
  241.      * @return boolean
  242.      */
  243.     protected function isLastStep()
  244.     {
  245.         return $this->lastStep;
  246.     }
  247.     /**
  248.      * Finalize task (last step)
  249.      */
  250.     protected function finalizeTask()
  251.     {
  252.         $this->release();
  253.         $this->close();
  254.     }
  255.     /**
  256.      * Finalize step
  257.      */
  258.     protected function finalizeStep()
  259.     {
  260.     }
  261.     /**
  262.      * Check availability
  263.      *
  264.      * @return boolean
  265.      */
  266.     protected function isValid()
  267.     {
  268.         return true;
  269.     }
  270.     /**
  271.      * Close task
  272.      */
  273.     protected function close()
  274.     {
  275.         \XLite\Core\Database::getEM()->remove($this->model);
  276.     }
  277.     protected function dispatchEvent(string $statusstring $message ''): void
  278.     {
  279.         $this->getEventDispatcher()->dispatch(new CronTaskEvent($this->prepareTaskDTO($status$message)), CronTaskEvent::NAME);
  280.     }
  281.     protected function prepareTaskDTO(string $statusstring $message ''): TaskDTO
  282.     {
  283.         return new TaskDTO(
  284.             $this->cronEventId,
  285.             static::class,
  286.             $this->getTitle(),
  287.             $status,
  288.             $message
  289.         );
  290.     }
  291.     protected function getEventDispatcher(): EventDispatcherInterface
  292.     {
  293.         return Container::getContainer()?->get('event_dispatcher');
  294.     }
  295. }