mirror of
https://github.com/pronamic/woocommerce-subscriptions.git
synced 2025-10-17 14:52:56 +00:00
149 lines
4.6 KiB
PHP
149 lines
4.6 KiB
PHP
<?php
|
|
/**
|
|
* Debug Tool with methods to update data in the background
|
|
*
|
|
* Add tools for debugging and managing Subscriptions to the
|
|
* WooCommerce > System Status > Tools administration screen.
|
|
*
|
|
* @author Prospress
|
|
* @category Admin
|
|
* @package WooCommerce Subscriptions/Admin
|
|
* @version 2.3
|
|
* @since 2.3
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
} // Exit if accessed directly
|
|
|
|
/**
|
|
* WCS_Background_Updater Class
|
|
*
|
|
* Provide APIs for a debug tool to update data in the background using Action Scheduler.
|
|
*/
|
|
abstract class WCS_Background_Updater {
|
|
|
|
/**
|
|
* @var int The amount of time, in seconds, to give the background process to run the update.
|
|
*/
|
|
protected $time_limit;
|
|
|
|
/**
|
|
* @var string The hook used to schedule background updates.
|
|
*/
|
|
protected $scheduled_hook;
|
|
|
|
/**
|
|
* Attach callbacks to hooks
|
|
*/
|
|
public function init() {
|
|
|
|
// Make sure child classes have defined a scheduled hook, otherwise we can't do background updates.
|
|
if ( is_null( $this->scheduled_hook ) ) {
|
|
throw new RuntimeException( __CLASS__ . ' must assign a hook to $this->scheduled_hook' );
|
|
}
|
|
|
|
if ( is_null( $this->time_limit ) ) {
|
|
|
|
$this->time_limit = 60;
|
|
|
|
// Allow more time for CLI requests, as they're not beholden to script timeouts
|
|
if ( $this->is_wp_cli_request() ) {
|
|
$this->time_limit *= 3;
|
|
}
|
|
}
|
|
|
|
// Allow for each class's time limit to be customised by 3rd party code, as well as all tools' time limits
|
|
$this->time_limit = apply_filters( 'wcs_debug_tools_time_limit', $this->time_limit, $this );
|
|
|
|
// Action scheduled in Action Scheduler for updating data in the background
|
|
add_action( $this->scheduled_hook, array( $this, 'run_update' ) );
|
|
}
|
|
|
|
/**
|
|
* Get the items to be updated, if any.
|
|
*
|
|
* @return array An array of items to update, or empty array if there are no items to update.
|
|
*/
|
|
abstract protected function get_items_to_update();
|
|
|
|
/**
|
|
* Run the update for a single item.
|
|
*
|
|
* @param mixed $item The item to update.
|
|
*/
|
|
abstract protected function update_item( $item );
|
|
|
|
/**
|
|
* Update a set of items in the background.
|
|
*
|
|
* This method will loop over until there are no more items to update, or the process has been running for the
|
|
* time limit set on the class @see $this->time_limit, which is 60 seconds by default (wall clock time, not
|
|
* execution time).
|
|
*
|
|
* The $scheduler_hook is rescheduled before updating any items something goes wrong when processing a batch - it's
|
|
* scheduled for $this->time_limit in future, so there's little chance of duplicate processes running at the same
|
|
* time with WP Cron, but importantly, there is some chance so it should not be used for critical data, like
|
|
* payments. Instead, it is intended for use for things like cache updates. It's also a good idea to use an atomic
|
|
* update methods to avoid updating something that has already been updated in a separate request.
|
|
*
|
|
* Importantly, the overlap between the next scheduled update and the current batch is also useful for running
|
|
* Action Scheduler via WP CLI, because it will allow for continuous execution of updates (i.e. updating a new
|
|
* batch as soon as one batch has execeeded the time limit rather than having to run Action Scheduler via WP CLI
|
|
* again later).
|
|
*/
|
|
public function run_update() {
|
|
|
|
$this->schedule_background_update();
|
|
|
|
// If the update is being run via WP CLI, we don't need to worry about the request time, just the processing time for this method
|
|
$start_time = $this->is_wp_cli_request() ? gmdate( 'U' ) : WCS_INIT_TIMESTAMP;
|
|
|
|
do {
|
|
|
|
$items = $this->get_items_to_update();
|
|
|
|
foreach ( $items as $item ) {
|
|
|
|
$this->update_item( $item );
|
|
|
|
$time_elapsed = ( gmdate( 'U' ) - $start_time );
|
|
|
|
if ( $time_elapsed >= $this->time_limit ) {
|
|
break 2;
|
|
}
|
|
}
|
|
} while ( ! empty( $items ) );
|
|
|
|
// If we stopped processing the batch because we ran out of items to process, not because we ran out of time, we don't need to run any other batches
|
|
if ( empty( $items ) ) {
|
|
$this->unschedule_background_updates();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Schedule the instance's hook to run in $this->time_limit seconds, if it's not already scheduled.
|
|
*/
|
|
protected function schedule_background_update() {
|
|
if ( false === wc_next_scheduled_action( $this->scheduled_hook ) ) {
|
|
wc_schedule_single_action( gmdate( 'U' ) + $this->time_limit, $this->scheduled_hook );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unschedule the instance's hook in Action Scheduler
|
|
*/
|
|
protected function unschedule_background_updates() {
|
|
wc_unschedule_action( $this->scheduled_hook );
|
|
}
|
|
|
|
/**
|
|
* Check whether the current request is via WP CLI
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function is_wp_cli_request() {
|
|
return ( defined( 'WP_CLI' ) && WP_CLI );
|
|
}
|
|
}
|