HEX
Server: nginx/1.18.0
System: Linux iZj6c1ieg2jrpk1z5tzi19Z 6.3.9-1.el7.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 21 22:18:40 EDT 2023 x86_64
User: www (1001)
PHP: 8.2.4
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.cytocare.cn/wp-content/plugins/mailpoet/lib/Cron/CronWorkerRunner.php
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing

namespace MailPoet\Cron;

if (!defined('ABSPATH')) exit;


use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Logging\LoggerFactory;
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
use MailPoetVendor\Carbon\Carbon;

class CronWorkerRunner {
  const TASK_BATCH_SIZE = 5;
  const TASK_RUN_TIMEOUT = 120;
  const TIMED_OUT_TASK_RESCHEDULE_TIMEOUT = 5;

  /** @var float */
  private $timer;

  /** @var CronHelper */
  private $cronHelper;

  /** @var CronWorkerScheduler */
  private $cronWorkerScheduler;

  /** @var ScheduledTasksRepository */
  private $scheduledTasksRepository;

  /** @var LoggerFactory  */
  private $loggerFactory;

  public function __construct(
    CronHelper $cronHelper,
    CronWorkerScheduler $cronWorkerScheduler,
    ScheduledTasksRepository $scheduledTasksRepository,
    LoggerFactory $loggerFactory
  ) {
    $this->timer = microtime(true);
    $this->cronHelper = $cronHelper;
    $this->cronWorkerScheduler = $cronWorkerScheduler;
    $this->scheduledTasksRepository = $scheduledTasksRepository;
    $this->loggerFactory = $loggerFactory;
  }

  public function run(CronWorkerInterface $worker) {
    // abort if execution limit is reached
    $this->cronHelper->enforceExecutionLimit($this->timer);
    $dueTasks = $this->getDueTasks($worker);
    $runningTasks = $this->getRunningTasks($worker);

    if (!$worker->checkProcessingRequirements()) {
      foreach (array_merge($dueTasks, $runningTasks) as $task) {
        $this->scheduledTasksRepository->remove($task);
        $this->scheduledTasksRepository->flush();
      }
      return false;
    }

    $worker->init();

    if (!$dueTasks && !$runningTasks) {
      if ($worker->scheduleAutomatically()) {
        $this->cronWorkerScheduler->schedule($worker->getTaskType(), $worker->getNextRunDate());
      }
      return false;
    }

    try {
      foreach ($dueTasks as $task) {
        $this->prepareTask($worker, $task);
      }
      // Re-fetch running tasks so that we can process tasks that were just prepared
      $runningTasks = $this->getRunningTasks($worker);
      foreach ($runningTasks as $task) {
        $this->processTask($worker, $task);
      }
    } catch (\Exception $e) {
      if (isset($task) && $task && $e->getCode() !== CronHelper::DAEMON_EXECUTION_LIMIT_REACHED) {
        /**
         * ToDo: Use \LoggerFactory::TOPIC_CRON as logger topic, once it is available
         */
        $this->loggerFactory->getLogger()->error($e->getMessage(), ['error' => $e]);
        $this->cronWorkerScheduler->rescheduleProgressively($task);
      }
      throw $e;
    }

    return true;
  }

  private function getDueTasks(CronWorkerInterface $worker) {
    return $this->scheduledTasksRepository->findDueByType($worker->getTaskType(), self::TASK_BATCH_SIZE);
  }

  private function getRunningTasks(CronWorkerInterface $worker) {
    return $this->scheduledTasksRepository->findRunningByType($worker->getTaskType(), self::TASK_BATCH_SIZE);
  }

  private function prepareTask(CronWorkerInterface $worker, ScheduledTaskEntity $task) {
    // abort if execution limit is reached
    $this->cronHelper->enforceExecutionLimit($this->timer);

    $prepareCompleted = $worker->prepareTaskStrategy($task, $this->timer);

    if ($prepareCompleted) {
      $task->setStatus(null);
      $this->scheduledTasksRepository->persist($task);
      $this->scheduledTasksRepository->flush();
    }
  }

  private function processTask(CronWorkerInterface $worker, ScheduledTaskEntity $task) {
    // abort if execution limit is reached
    $this->cronHelper->enforceExecutionLimit($this->timer);

    if (!$worker->supportsMultipleInstances()) {
      if ($this->rescheduleOutdated($task)) {
        return false;
      }
      if ($this->isInProgress($task)) {
        return false;
      }
    }

    $this->startProgress($task);

    try {
      $completed = $worker->processTaskStrategy($task, $this->timer);
    } catch (\Exception $e) {
      $this->stopProgress($task);
      throw $e;
    }

    if ($completed) {
      $this->complete($task);
    }

    $this->stopProgress($task);

    return (bool)$completed;
  }

  private function rescheduleOutdated(ScheduledTaskEntity $task) {
    $currentTime = Carbon::now()->millisecond(0);

    if (empty($task->getUpdatedAt())) {
      // missing updatedAt, consider this task outdated (set year to 2000) and reschedule
      $updatedAt = Carbon::createFromDate(2000);
    } else if (!$task->getUpdatedAt() instanceof Carbon) {
      $updatedAt = new Carbon($task->getUpdatedAt());
    } else {
      $updatedAt = $task->getUpdatedAt();
    }

    // If the task is running for too long consider it stuck and reschedule
    if (!empty($task->getUpdatedAt()) && $updatedAt->diffInMinutes($currentTime, false) > self::TASK_RUN_TIMEOUT) {
      $this->stopProgress($task);
      $this->cronWorkerScheduler->reschedule($task, self::TIMED_OUT_TASK_RESCHEDULE_TIMEOUT);
      return true;
    }
    return false;
  }

  private function isInProgress(ScheduledTaskEntity $task) {
    if ($task->getInProgress()) {
      // Do not run multiple instances of the task
      return true;
    }
    return false;
  }

  private function startProgress(ScheduledTaskEntity $task) {
    $task->setInProgress(true);
    $this->scheduledTasksRepository->persist($task);
    $this->scheduledTasksRepository->flush();
  }

  private function stopProgress(ScheduledTaskEntity $task) {
    $task->setInProgress(false);
    $this->scheduledTasksRepository->persist($task);
    $this->scheduledTasksRepository->flush();
  }

  private function complete(ScheduledTaskEntity $task) {
    $task->setProcessedAt(Carbon::now()->millisecond(0));
    $task->setStatus(ScheduledTaskEntity::STATUS_COMPLETED);
    $this->scheduledTasksRepository->persist($task);
    $this->scheduledTasksRepository->flush();
  }
}