geno/wp-content/plugins/mailpoet/lib/Cron/Triggers/WordPress.php
2024-02-01 11:54:18 +00:00

274 lines
9.4 KiB
PHP

<?php declare(strict_types = 1);
namespace MailPoet\Cron\Triggers;
if (!defined('ABSPATH')) exit;
use MailPoet\Config\ServicesChecker;
use MailPoet\Cron\CronHelper;
use MailPoet\Cron\Supervisor;
use MailPoet\Cron\Workers\Beamer as BeamerWorker;
use MailPoet\Cron\Workers\Bounce as BounceWorker;
use MailPoet\Cron\Workers\KeyCheck\PremiumKeyCheck as PremiumKeyCheckWorker;
use MailPoet\Cron\Workers\KeyCheck\SendingServiceKeyCheck as SendingServiceKeyCheckWorker;
use MailPoet\Cron\Workers\Scheduler as SchedulerWorker;
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
use MailPoet\Cron\Workers\SubscribersStatsReport;
use MailPoet\Cron\Workers\WorkersFactory;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Mailer\MailerLog;
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsController;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Doctrine\ORM\EntityManager;
class WordPress {
const SCHEDULED_IN_THE_PAST = 'past';
const SCHEDULED_IN_THE_FUTURE = 'future';
const RUN_INTERVAL = -1; // seconds
const LAST_RUN_AT_SETTING = 'cron_trigger_wordpress.last_run_at';
private $tasksCounts;
/** @var CronHelper */
private $cronHelper;
/** @var Supervisor */
private $supervisor;
/** @var SettingsController */
private $settings;
/** @var WPFunctions */
private $wp;
/** @var ServicesChecker */
private $serviceChecker;
/** @var ScheduledTasksRepository */
private $scheduledTasksRepository;
/** @var EntityManager */
private $entityManager;
public function __construct(
CronHelper $cronHelper,
Supervisor $supervisor,
SettingsController $settings,
ServicesChecker $serviceChecker,
WPFunctions $wp,
ScheduledTasksRepository $scheduledTasksRepository,
EntityManager $entityManager
) {
$this->supervisor = $supervisor;
$this->settings = $settings;
$this->wp = $wp;
$this->cronHelper = $cronHelper;
$this->serviceChecker = $serviceChecker;
$this->scheduledTasksRepository = $scheduledTasksRepository;
$this->entityManager = $entityManager;
}
public function run() {
if (!$this->checkRunInterval()) {
return false;
}
if (!$this->checkExecutionRequirements()) {
$this->stop();
return;
}
$this->supervisor->init();
return $this->supervisor->checkDaemon();
}
private function checkRunInterval(): bool {
$runInterval = $this->wp->applyFilters('mailpoet_cron_trigger_wordpress_run_interval', self::RUN_INTERVAL);
if ($runInterval === -1) {
return true;
}
$lastRunAt = (int)$this->settings->get(self::LAST_RUN_AT_SETTING, 0);
$runIntervalElapsed = (time() - $lastRunAt) >= $runInterval;
if ($runIntervalElapsed) {
$this->settings->set(self::LAST_RUN_AT_SETTING, time());
return true;
}
return false;
}
public static function resetRunInterval(): void {
$settings = SettingsController::getInstance();
$settings->set(self::LAST_RUN_AT_SETTING, 0);
}
public function checkExecutionRequirements(): bool {
$this->loadTasksCounts();
// Because a lot of workers has the same pattern for check if it's active we can use a loop here
$isSimpleWorkerActive = false;
foreach (WorkersFactory::SIMPLE_WORKER_TYPES as $simpleWorkerType) {
$tasksCount = $this->getTasksCount([
'type' => $simpleWorkerType,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
if ($tasksCount) {
$isSimpleWorkerActive = true;
break;
}
}
return (
$this->isSendingQueueActive()
|| $this->isBounceActive()
|| $this->isSendingServiceKeyCheckActive()
|| $this->isPremiumKeyCheckActive()
|| $this->isSubscriberStatsReportActive()
|| $this->isBeamerCheckActive()
|| $isSimpleWorkerActive
);
}
public function stop() {
$cronDaemon = $this->cronHelper->getDaemon();
if ($cronDaemon) {
$this->cronHelper->deactivateDaemon($cronDaemon);
}
}
private function isSendingQueueActive(): bool {
$scheduledQueues = $this->scheduledTasksRepository->findScheduledSendingTasks(SchedulerWorker::TASK_BATCH_SIZE);
$runningQueues = $this->scheduledTasksRepository->findRunningSendingTasks(SendingQueueWorker::TASK_BATCH_SIZE);
$sendingLimitReached = MailerLog::isSendingLimitReached();
$sendingIsPaused = MailerLog::isSendingPaused();
$sendingWaitingForRetry = MailerLog::isSendingWaitingForRetry();
return (($scheduledQueues || $runningQueues) && !$sendingLimitReached && !$sendingIsPaused && !$sendingWaitingForRetry);
}
private function isBounceActive(): bool {
$mpSendingEnabled = Bridge::isMPSendingServiceEnabled();
$bounceDueTasks = $this->getTasksCount([
'type' => BounceWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
$bounceFutureTasks = $this->getTasksCount([
'type' => BounceWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_FUTURE],
'status' => [ScheduledTaskEntity::STATUS_SCHEDULED],
]);
return ($mpSendingEnabled && ($bounceDueTasks || !$bounceFutureTasks));
}
private function isSendingServiceKeyCheckActive(): bool {
$mpSendingEnabled = Bridge::isMPSendingServiceEnabled();
$msskeycheckDueTasks = $this->getTasksCount([
'type' => SendingServiceKeyCheckWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
$msskeycheckFutureTasks = $this->getTasksCount([
'type' => SendingServiceKeyCheckWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_FUTURE],
'status' => [ScheduledTaskEntity::STATUS_SCHEDULED],
]);
return ($mpSendingEnabled && ($msskeycheckDueTasks || !$msskeycheckFutureTasks));
}
private function isPremiumKeyCheckActive(): bool {
$premiumKeySpecified = Bridge::isPremiumKeySpecified();
$premiumKeycheckDueTasks = $this->getTasksCount([
'type' => PremiumKeyCheckWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
$premiumKeycheckFutureTasks = $this->getTasksCount([
'type' => PremiumKeyCheckWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_FUTURE],
'status' => [ScheduledTaskEntity::STATUS_SCHEDULED],
]);
return ($premiumKeySpecified && ($premiumKeycheckDueTasks || !$premiumKeycheckFutureTasks));
}
private function isSubscriberStatsReportActive(): bool {
$validAccountKey = $this->serviceChecker->getValidAccountKey();
$statsReportDueTasks = $this->getTasksCount([
'type' => SubscribersStatsReport::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
$statsReportFutureTasks = $this->getTasksCount([
'type' => SubscribersStatsReport::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_FUTURE],
'status' => [ScheduledTaskEntity::STATUS_SCHEDULED],
]);
return ($validAccountKey && ($statsReportDueTasks || !$statsReportFutureTasks));
}
private function isBeamerCheckActive(): bool {
$beamerDueChecks = $this->getTasksCount([
'type' => BeamerWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_PAST],
'status' => ['null', ScheduledTaskEntity::STATUS_SCHEDULED],
]);
$beamerFutureChecks = $this->getTasksCount([
'type' => BeamerWorker::TASK_TYPE,
'scheduled_in' => [self::SCHEDULED_IN_THE_FUTURE],
'status' => [ScheduledTaskEntity::STATUS_SCHEDULED],
]);
return $beamerDueChecks || !$beamerFutureChecks;
}
private function loadTasksCounts(): void {
$scheduledTasksTableName = $this->entityManager->getClassMetadata(ScheduledTaskEntity::class)->getTableName();
$sql = "
SELECT
type,
status,
count(*) AS count,
CASE WHEN scheduled_at <= :now THEN :past ELSE :future END AS scheduled_in
FROM $scheduledTasksTableName
WHERE deleted_at IS NULL AND (status != :statusCompleted OR status IS NULL)
GROUP BY type, status, scheduled_in";
$stmt = $this->entityManager->getConnection()->prepare($sql);
$stmt->bindValue('now', date('Y-m-d H:i:s', $this->wp->currentTime('timestamp')));
$stmt->bindValue('past', self::SCHEDULED_IN_THE_PAST);
$stmt->bindValue('future', self::SCHEDULED_IN_THE_FUTURE);
$stmt->bindValue('statusCompleted', ScheduledTaskEntity::STATUS_COMPLETED);
$rows = $stmt->executeQuery()->fetchAllAssociative();
$this->tasksCounts = [];
foreach ($rows as $r) {
if (empty($this->tasksCounts[$r['type']])) {
$this->tasksCounts[$r['type']] = [];
}
if (empty($this->tasksCounts[$r['type']][$r['scheduled_in']])) {
$this->tasksCounts[$r['type']][$r['scheduled_in']] = [];
}
$this->tasksCounts[$r['type']][$r['scheduled_in']][$r['status'] ?: 'null'] = $r['count'];
}
}
private function getTasksCount(array $options): int {
$count = 0;
$type = $options['type'];
foreach ($options['scheduled_in'] as $scheduledIn) {
foreach ($options['status'] as $status) {
if (!empty($this->tasksCounts[$type][$scheduledIn][$status])) {
$count += $this->tasksCounts[$type][$scheduledIn][$status];
}
}
}
return $count;
}
}