Files
grzanieplus.pl/plugins/stTaskScheluderPlugin/lib/task/stTask.class.php
2025-03-12 17:06:23 +01:00

498 lines
12 KiB
PHP

<?php
abstract class stTask implements stTaskInterface
{
/**
* Status "oczekuje"
*/
const STATUS_PENDING = 0;
/**
* Status "w trakcie wykonywania"
*/
const STATUS_RUNNING = 1;
/**
* Cron job logger instance
*
* @var stTaskLoggerInterface
*/
private $logger;
/**
* Instancja modelu zadania
*
* @var Task
*/
private $task;
/**
* Undocumented variable
*
* @var stEventDispatcher
*/
private $dispatcher;
/**
* Aktualny postęp wykonywania zadania w %
*
* @var int
*/
private $progress = 0;
/**
* Task options
*
* @var array
*/
private $options = array();
/**
* Własne zdefiniowane parametry zapamietywane na czas trwania zadania
*
* @var array
*/
private $parameters = array();
private $initialized = null;
public function __toString()
{
return $this->getName();
}
public function isCLI(): bool
{
return $this->getParameter('cli', false);
}
public function __construct(Task $task, stTaskLoggerInterface $logger, stEventDispatcher $dispatcher)
{
$this->task = $task;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
}
final public function doInitialize(bool $reset = false)
{
if ($reset)
{
if (null === $this->task->getLastOffset() || $this->task->getLastOffset() >= $this->task->getLastCount())
{
$this->task->setLastProgress(0);
$this->task->setLastOffset(0);
$this->task->setLastCount(null);
$this->task->save();
}
}
if (null === $this->initialized)
{
$result = $this->initialize();
$this->initialized = null !== $result ? $result : true;
}
return $this->initialized;
}
public function initialize()
{
}
final public function setLogger(stTaskLoggerInterface $logger): void
{
$this->logger = $logger;
}
/**
* Ustawia dodatkowe opcje dla zadania
*
* @param array $options
* @return void
*/
final public function setOptions(array $options): void
{
$this->options = $options;
}
/**
* Ustawia parametr zapamiętywany na czas trwania zadania
*
* @param string $name Nazwa parametru
* @param mixed $value Wartość parametru
* @return void
*/
final public function setParameter(string $name, $value): void
{
$this->parameters[$name] = $value;
}
/**
* Pobiera parametr
*
* @param string $name Nazwa parametru
* @param mixed $default Wartość domyślna jaka ma być zwrócona w przypadku braku ustawionego parametru
* @return mixed
*/
final public function getParameter(string $name, $default = null)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : $default;
}
/**
* Zwraca tablice zapamietanych parametrów
*
* @return array
*/
final public function getParameters(): array
{
return $this->parameters;
}
/**
* Ustawia parametry do zapamietania na czas trwania zadania
*
* @param array $parameters Tablica parametrów
* @return void
*/
final public function setParameters(array $parameters): void
{
$this->parameters = $parameters;
}
/**
* Zwraca logger dla zadania
*
* @return stTaskLoggerInterface
*/
final public function getLogger(): stTaskLoggerInterface
{
return $this->logger;
}
/**
* Zwraca nazwę zadania
*
* @return string
*/
final public function getName(): string
{
return $this->task->getName();
}
/**
* Odświeża status aktywności zadania
* Status aktywności odświeżany jest średnio co 5 minut
* Jeżeli metoda execute zadania wykonuje się dłużej niż 5 minut konieczne jest wywoływanie tej metody ręcznie
* w innym wypadku zadanie zostanie uznane jako nieaktywne i będzie zostanie uruchomione ponownie
*
* @return void
*/
final public function refreshActiveStatus(): void
{
$this->task->setLastActiveAt(time());
$this->task->save();
}
/**
* Zwraca identyfikator zadania
*
* @return string
*/
final public function getId(): string
{
return $this->task->getTaskId();
}
/**
* Zwraca odstęp czasowy w sekundach co jaki będzie wykonywane zadanie
*
* @return int
*/
final public function getTimeInterval(): int
{
return $this->task->getTimeInterval();
}
/**
* Zwraca godzinę o jakiej zostanie wykonane zadanie
*
* @return string
*/
final public function getExecuteAt(): string
{
return $this->task->getExecuteAt();
}
/**
* Zwraca aktualny status zadania
*
* @return int
*/
final public function getStatus(): int
{
return $this->task->getStatus();
}
/**
* Zwraca instancje modelu zadania
*
* @return Task
*/
final public function getTask(): Task
{
return $this->task;
}
/**
* Sprawdza czy zadanie jest gotowe do wykonania
*
* @return bool
*/
final public function isReadyToExecute(bool $checkTime = true): bool
{
$ready = $this->task->getIsActive() && $this->getStatus() == self::STATUS_PENDING && (!$checkTime || null === $this->task->getLastExecutedAt() || strtotime($this->task->getNextExecuteDate()) <= time());
if (!$ready && $this->getStatus() != self::STATUS_PENDING && time() - strtotime($this->task->getLastActiveAt()) >= stTaskConfiguration::TIME_INTERVAL_10MIN)
{
$ready = true;
}
return $ready;
}
final public function getProgress(): int
{
return $this->progress;
}
final public function getLastProgress(): int
{
return $this->task->getLastProgress();
}
final public function getOffset(): int
{
return null !== $this->task->getLastOffset() ? $this->task->getLastOffset() : 0;
}
/**
* Zwraca ilość do wykonania
*
* @return int
*/
final public function doCount(bool $forceRefresh = false): int
{
try
{
if (null === $this->task->getLastCount() || false !== $forceRefresh)
{
$count = $this->count();
if (!$count)
{
$this->getLogger()->info("Zadanie nie wymagało wykonania");
$this->task->setLastExecutedAt(time());
$this->task->setLastFinishedAt(time());
$this->task->save();
$this->setParameter('clear_cache', false);
}
$this->task->setLastCount($count);
$this->task->save();
}
}
catch (Throwable $e)
{
if (!mysqli_ping(Propel::getConnection()->getResource()))
{
Propel::close();
Propel::initialize();
}
try
{
$this->getLogger()->exception($e);
return 0;
}
catch (Throwable $e)
{
return 0;
}
}
return $this->task->getLastCount();
}
/**
* Zmienia status zadania jako "w trakcie wykonywania"
*
* @return void
*/
final private function doStart()
{
$this->task->doStart();
$this->getLogger()->info("Rozpoczęcie wykonywania");
$this->dispatcher->notify(new sfEvent($this->task, 'task.started'));
$this->started();
}
/**
* Ustawia zadanie jako zakończone
*
* @return void
*/
final public function doFinish(): void
{
$this->task->doFinish();
$this->getLogger()->info("Zakończenie wykonywania");
$this->dispatcher->notify(new sfEvent($this->task, 'task.finished'));
$this->finished();
if (false !== $this->getParameter('clear_cache'))
{
$this->clearCache();
}
}
/**
* Czyści pamięć podręczną aplikacji
*
* @return void
*/
public function clearCache()
{
stFunctionCache::clearAll();
stPartialCache::clearAll('frontend');
stFastCacheManager::clearCache();
}
/**
* This method is executed once when the task starts
*
* @return void
*/
public function started()
{
}
/**
* This method is executed once when the task ends
*
* @return void
*/
public function finished()
{
}
/**
* Wykonuje zadanie
*
* @param integer $offset Aktualnie przesunięcie od jakiego ma być wykonuwane zadanie
* @return integer Zwraca kolejne przesunięcie od jakiego ma być wykonywane zadanie
*/
final public function doExecute(int $offset): int
{
try
{
if (!$offset)
{
$this->doStart();
}
if (isset($this->options['unexpected-terminate']))
{
exit;
}
$this->disabledCacheClear();
$offset = $this->execute($offset);
$this->enableClearCache();
$this->task->setLastOffset($offset);
if (null === $this->task->getLastActiveAt() || time() - strtotime($this->task->getLastActiveAt()) >= stTaskConfiguration::TIME_INTERVAL_5MIN)
{
$this->refreshActiveStatus();
}
/**
* Poprawka wycieku pamięci
*/
ProductOptionsValue::clearStaticPool();
stNewProductOptions::clearStaticPool();
sfAsset::clearStaticPool();
if ($offset > $this->doCount())
{
$offset = $this->doCount();
}
$this->progress = intval(($offset / $this->doCount()) * 100);
if ($this->progress >= 25 && $this->task->getLastProgress() + 25 <= $this->progress)
{
$this->task->setLastProgress($this->progress);
$this->getLogger()->info("Wykonano %progress%%", array("%progress%" => $this->progress));
$this->dispatcher->notify(new sfEvent($this, 'task.progress'));
}
if ($offset >= $this->doCount())
{
$this->doFinish();
return $this->doCount();
}
}
catch (Throwable $e)
{
if (!mysqli_ping(Propel::getConnection()->getResource()))
{
Propel::close();
Propel::initialize();
try
{
$this->doCount();
$this->getLogger()->warning("Pomyślne przywrócenie utraconego połączenia MySQL");
return $offset;
}
catch (Throwable $e)
{
Propel::close();
Propel::initialize();
$this->getLogger()->error("Wystąpił błąd podczas próby wznawiania utraconego połączenia MySQL");
}
}
$this->getLogger()->exception($e);
$this->doFinish();
return $this->doCount();
}
if (!$this->isCLI())
{
$this->task->save();
}
return $offset;
}
private function disabledCacheClear()
{
stFastCacheManager::disableClearCache();
stFunctionCache::disableClearCache(array('stTax'));
stPartialCache::disableClearCache();
}
private function enableClearCache()
{
stFastCacheManager::enableClearCache();
stFunctionCache::enableClearCache();
stPartialCache::enableClearCache();
}
}