<?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Jobs;
use Automattic\WooCommerce\GoogleListingsAndAds\ActionScheduler\ActionSchedulerInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Class AbstractActionSchedulerJob
*
* Abstract class for jobs that use ActionScheduler.
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Jobs
*/
abstract class AbstractActionSchedulerJob implements ActionSchedulerJobInterface {
use PluginHelper;
/**
* @var ActionSchedulerInterface
*/
protected $action_scheduler;
/**
* @var ActionSchedulerJobMonitor
*/
protected $monitor;
/**
* Whether the job should be rescheduled on timeout.
*
* @var bool
*/
protected $retry_on_timeout = true;
/**
* AbstractActionSchedulerJob constructor.
*
* @param ActionSchedulerInterface $action_scheduler
* @param ActionSchedulerJobMonitor $monitor
*/
public function __construct( ActionSchedulerInterface $action_scheduler, ActionSchedulerJobMonitor $monitor ) {
$this->action_scheduler = $action_scheduler;
$this->monitor = $monitor;
}
/**
* Init the batch schedule for the job.
*
* The job name is used to generate the schedule event name.
*/
public function init(): void {
add_action( $this->get_process_item_hook(), [ $this, 'handle_process_items_action' ] );
}
/**
* Can the job be scheduled.
*
* @param array|null $args
*
* @return bool Returns true if the job can be scheduled.
*/
public function can_schedule( $args = [] ): bool {
return ! $this->is_running( $args );
}
/**
* Handles processing single item action hook.
*
* @hooked gla/jobs/{$job_name}/process_item
*
* @param array $items The job items from the current batch.
*
* @throws Exception If an error occurs.
*/
public function handle_process_items_action( array $items = [] ) {
$process_hook = $this->get_process_item_hook();
$process_args = [ $items ];
$this->monitor->validate_failure_rate( $this, $process_hook, $process_args );
if ( $this->retry_on_timeout ) {
$this->monitor->attach_timeout_monitor( $process_hook, $process_args );
}
try {
$this->process_items( $items );
} catch ( Exception $exception ) {
// reschedule on failure
$this->action_scheduler->schedule_immediate( $process_hook, $process_args );
// throw the exception again so that it can be logged
throw $exception;
}
$this->monitor->detach_timeout_monitor( $process_hook, $process_args );
}
/**
* Check if this job is running.
*
* The job is considered to be running if the "process_item" action is currently pending or in-progress.
*
* @param array|null $args
*
* @return bool
*/
protected function is_running( ?array $args = [] ): bool {
return $this->action_scheduler->has_scheduled_action( $this->get_process_item_hook(), $args );
}
/**
* Get the base name for the job's scheduled actions.
*
* @return string
*/
protected function get_hook_base_name(): string {
return "{$this->get_slug()}/jobs/{$this->get_name()}/";
}
/**
* Get the hook name for the "process item" action.
*
* This method is required by the job monitor.
*
* @return string
*/
public function get_process_item_hook(): string {
return "{$this->get_hook_base_name()}process_item";
}
/**
* Process batch items.
*
* @param array $items A single batch from the get_batch() method.
*
* @throws Exception If an error occurs. The exception will be logged by ActionScheduler.
*/
abstract protected function process_items( array $items );
}