Files
piratepoet/mailpoet/lib/Cron/DaemonActionSchedulerRunner.php
2022-08-03 10:36:57 +02:00

136 lines
4.5 KiB
PHP

<?php declare(strict_types=1);
namespace MailPoet\Cron;
use MailPoet\Cron\ActionScheduler\ActionScheduler;
use MailPoet\Cron\Triggers\WordPress;
use MailPoet\WP\Functions as WPFunctions;
class DaemonActionSchedulerRunner {
const DAEMON_RUN_SCHEDULER_ACTION = 'mailpoet/cron/daemon-run';
const DAEMON_TRIGGER_SCHEDULER_ACTION = 'mailpoet/cron/daemon-trigger';
const RUN_ACTION_SCHEDULER = 'mailpoet-cron-action-scheduler-run';
/** @var Daemon */
private $daemon;
/** @var WordPress */
private $wordpressTrigger;
/** @var CronHelper */
private $cronHelper;
/** @var WPFunctions */
private $wp;
/** @var ActionScheduler */
private $actionScheduler;
public function __construct(
Daemon $daemon,
CronHelper $cronHelper,
WordPress $wordpressTrigger,
WPFunctions $wp,
ActionScheduler $actionScheduler
) {
$this->cronHelper = $cronHelper;
$this->daemon = $daemon;
$this->wordpressTrigger = $wordpressTrigger;
$this->wp = $wp;
$this->actionScheduler = $actionScheduler;
}
public function init() {
$this->wp->addAction(self::DAEMON_RUN_SCHEDULER_ACTION, [$this, 'run']);
$this->wp->addAction(self::DAEMON_TRIGGER_SCHEDULER_ACTION, [$this, 'trigger']);
$this->wp->addAction('wp_ajax_nopriv_' . self::RUN_ACTION_SCHEDULER, [$this, 'runActionScheduler'], 0);
if (!$this->actionScheduler->hasScheduledAction(self::DAEMON_TRIGGER_SCHEDULER_ACTION)) {
$this->actionScheduler->scheduleRecurringAction($this->wp->currentTime('timestamp'), 20, self::DAEMON_TRIGGER_SCHEDULER_ACTION);
}
}
public function deactivate() {
$this->actionScheduler->unscheduleAction(self::DAEMON_TRIGGER_SCHEDULER_ACTION);
$this->actionScheduler->unscheduleAction(self::DAEMON_RUN_SCHEDULER_ACTION);
}
/**
* In regular intervals checks if there are scheduled tasks to execute.
* In case there are tasks it spawns a recurring action.
* @return void
*/
public function trigger() {
$hasJobsToDo = $this->wordpressTrigger->checkExecutionRequirements();
if (!$hasJobsToDo) {
$this->actionScheduler->unscheduleAction(self::DAEMON_RUN_SCHEDULER_ACTION);
return;
}
if ($this->actionScheduler->hasScheduledAction(self::DAEMON_RUN_SCHEDULER_ACTION)) {
return;
}
// Start recurring action with minimal interval to ensure continuous execution of the daemon
$this->actionScheduler->scheduleRecurringAction($this->wp->currentTime('timestamp') - 1, 1, self::DAEMON_RUN_SCHEDULER_ACTION);
$this->triggerRemoteExecutor();
}
/**
* Run daemon that processes scheduled tasks for limited time (default 20 seconds)
*/
public function run(): void {
$this->wp->addAction('action_scheduler_after_process_queue', [$this, 'afterProcess']);
$this->daemon->run($this->cronHelper->createDaemon($this->cronHelper->createToken()));
}
/**
* This method is triggered by an ajax request.
* It creates ActionScheduler runner and process a batch actions that are ready to be processed
* @return void
*/
public function runActionScheduler() {
$this->wp->addFilter( 'action_scheduler_queue_runner_concurrent_batches', [$this, 'ensureConcurrency']);
\ActionScheduler_QueueRunner::instance()->run();
wp_die();
}
/**
* When triggering Action Runner we need to make sure we are able to trigger new runner from a runner
* @return int
*/
public function ensureConcurrency(int $concurrency): int {
return ($concurrency) < 2 ? 2 : $concurrency;
}
/**
* After Action Scheduler finishes queue always check there is more work to do and in case there is trigger additional runner.
*/
public function afterProcess() {
if ($this->wordpressTrigger->checkExecutionRequirements()) {
sleep(2); // Add short sleep to ensure next action ready to be processed since minimal schedule interval is 1 second
$this->triggerRemoteExecutor();
} else {
$this->actionScheduler->unscheduleAction(self::DAEMON_RUN_SCHEDULER_ACTION);
}
}
/**
* Spawns addition Action Scheduler runner
* @see https://actionscheduler.org/perf/#increasing-initialisation-rate-of-runners
* @return void
*/
private function triggerRemoteExecutor() {
$this->wp->addFilter('https_local_ssl_verify', '__return_false', 100);
$this->wp->wpRemotePost( $this->wp->adminUrl( 'admin-ajax.php' ), [
'method' => 'POST',
'timeout' => 5,
'redirection' => 5,
'httpversion' => '1.0',
'blocking' => false,
'headers' => [],
'body' => [
'action' => self::RUN_ACTION_SCHEDULER,
],
'cookies' => [],
]);
}
}