Skip to content

Commit

Permalink
Update WP background processing and Action Scheduler (#786)
Browse files Browse the repository at this point in the history
  • Loading branch information
remyperona authored Jan 29, 2024
1 parent de6dc98 commit b396de2
Show file tree
Hide file tree
Showing 27 changed files with 1,443 additions and 292 deletions.
19 changes: 12 additions & 7 deletions inc/Dependencies/ActionScheduler/action-scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* Description: A robust scheduling library for use in WordPress plugins.
* Author: Automattic
* Author URI: https://automattic.com/
* Version: 3.5.4
* Version: 3.7.1
* License: GPLv3
* Requires at least: 6.2
* Tested up to: 6.4
* Requires PHP: 5.6
*
* Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
*
Expand All @@ -26,27 +29,29 @@
* @package ActionScheduler
*/

if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
if ( ! function_exists( 'action_scheduler_register_3_dot_7_dot_1' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.

if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
}

add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION.
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_7_dot_1', 0, 0 ); // WRCS: DEFINED_VERSION.

// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
/**
* Registers this version of Action Scheduler.
*/
function action_scheduler_register_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
function action_scheduler_register_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
$versions = ActionScheduler_Versions::instance();
$versions->register( '3.5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION.
$versions->register( '3.7.1', 'action_scheduler_initialize_3_dot_7_dot_1' ); // WRCS: DEFINED_VERSION.
}

// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
/**
* Initializes this version of Action Scheduler.
*/
function action_scheduler_initialize_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
function action_scheduler_initialize_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
// A final safety check is required even here, because historic versions of Action Scheduler
// followed a different pattern (in some unusual cases, we could reach this point and the
// ActionScheduler class is already defined—so we need to guard against that).
Expand All @@ -58,7 +63,7 @@ function action_scheduler_initialize_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.

// Support usage in themes - load this version if no plugin has loaded a version yet.
if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
action_scheduler_initialize_3_dot_5_dot_4(); // WRCS: DEFINED_VERSION.
action_scheduler_initialize_3_dot_7_dot_1(); // WRCS: DEFINED_VERSION.
do_action( 'action_scheduler_pre_theme_init' );
ActionScheduler_Versions::initialize_latest_version();
}
Expand Down
60 changes: 60 additions & 0 deletions inc/Dependencies/ActionScheduler/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
*** Changelog ***

= 3.7.1 - 2023-12-13 =
* Release/3.7.0.
* Tweak - WP 6.4 compatibility.
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.

= 3.7.0 - 2023-11-20 =
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
* Add extended indexes for hook_status_scheduled_date_gmt and status_sheduled_date_gmt.
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
* Release/3.6.4.
* Tweak - WP 6.4 compatibility.
* Update unit tests for upcoming dependency version policy.
* make sure hook action_scheduler_failed_execution can access original exception object.
* mention dependency version policy in usage.md.

= 3.6.4 - 2023-10-11 =
* Performance improvements when bulk cancelling actions.
* Dev-related fixes.

= 3.6.3 - 2023-09-13 =
* Use `_doing_it_wrong` in initialization check.

= 3.6.2 - 2023-08-09 =
* Add guidance about passing arguments.
* Atomic option locking.
* Improve bulk delete handling.
* Include database error in the exception message.
* Tweak - WP 6.3 compatibility.

= 3.6.1 - 2023-06-14 =
* Document new optional `$priority` arg for various API functions.
* Document the new `--exclude-groups` WP CLI option.
* Document the new `action_scheduler_init` hook.
* Ensure actions within each claim are executed in the expected order.
* Fix incorrect text domain.
* Remove SHOW TABLES usage when checking if tables exist.

= 3.6.0 - 2023-05-10 =
* Add $unique parameter to function signatures.
* Add a cast-to-int for extra safety before forming new DateTime object.
* Add a hook allowing exceptions for consistently failing recurring actions.
* Add action priorities.
* Add init hook.
* Always raise the time limit.
* Bump minimatch from 3.0.4 to 3.0.8.
* Bump yaml from 2.2.1 to 2.2.2.
* Defensive coding relating to gaps in declared schedule types.
* Do not process an action if it cannot be set to `in-progress`.
* Filter view labels (status names) should be translatable | #919.
* Fix WPCLI progress messages.
* Improve data-store initialization flow.
* Improve error handling across all supported PHP versions.
* Improve logic for flushing the runtime cache.
* Support exclusion of multiple groups.
* Update lint-staged and Node/NPM requirements.
* add CLI clean command.
* add CLI exclude-group filter.
* exclude past-due from list table all filter count.
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.

= 3.5.4 - 2023-01-17 =
* Add pre filters during action registration.
* Async scheduling.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ class ActionScheduler_ActionFactory {
* @param array $args Args to pass to callbacks when the hook is triggered.
* @param ActionScheduler_Schedule $schedule The action's schedule.
* @param string $group A group to put the action in.
* phpcs:ignore Squiz.Commenting.FunctionComment.ExtraParamComment
* @param int $priority The action priority.
*
* @return ActionScheduler_Action An instance of the stored action.
*/
public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) {
// The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with
// third-party subclasses created before this param was added.
$priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10;

switch ( $status ) {
case ActionScheduler_Store::STATUS_PENDING:
Expand All @@ -36,17 +41,19 @@ public function get_stored_action( $status, $hook, array $args = array(), Action
$action_class = apply_filters( 'action_scheduler_stored_action_class', $action_class, $status, $hook, $args, $schedule, $group );

$action = new $action_class( $hook, $args, $schedule, $group );
$action->set_priority( $priority );

/**
* Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group.
*
* @param ActionScheduler_Action $action The instantiated action.
* @param string $hook The instantiated action's hook.
* @param array $args The instantiated action's args.
* @param ActionScheduler_Action $action The instantiated action.
* @param string $hook The instantiated action's hook.
* @param array $args The instantiated action's args.
* @param ActionScheduler_Schedule $schedule The instantiated action's schedule.
* @param string $group The instantiated action's group.
* @param string $group The instantiated action's group.
* @param int $priority The action priority.
*/
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group );
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority );
}

/**
Expand Down Expand Up @@ -229,9 +236,100 @@ public function repeat( $action ) {
$schedule_class = get_class( $schedule );
$new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
$new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
$new_action->set_priority( $action->get_priority() );
return $this->store( $new_action );
}

/**
* Creates a scheduled action.
*
* This general purpose method can be used in place of specific methods such as async(),
* async_unique(), single() or single_unique(), etc.
*
* @internal Not intended for public use, should not be overriden by subclasses.
*
* @param array $options {
* Describes the action we wish to schedule.
*
* @type string $type Must be one of 'async', 'cron', 'recurring', or 'single'.
* @type string $hook The hook to be executed.
* @type array $arguments Arguments to be passed to the callback.
* @type string $group The action group.
* @type bool $unique If the action should be unique.
* @type int $when Timestamp. Indicates when the action, or first instance of the action in the case
* of recurring or cron actions, becomes due.
* @type int|string $pattern Recurrence pattern. This is either an interval in seconds for recurring actions
* or a cron expression for cron actions.
* @type int $priority Lower values means higher priority. Should be in the range 0-255.
* }
*
* @return int The action ID. Zero if there was an error scheduling the action.
*/
public function create( array $options = array() ) {
$defaults = array(
'type' => 'single',
'hook' => '',
'arguments' => array(),
'group' => '',
'unique' => false,
'when' => time(),
'pattern' => null,
'priority' => 10,
);

$options = array_merge( $defaults, $options );

// Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability
// to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions).
if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) {
$options['type'] = 'single';
}

switch ( $options['type'] ) {
case 'async':
$schedule = new ActionScheduler_NullSchedule();
break;

case 'cron':
$date = as_get_datetime_object( $options['when'] );
$cron = CronExpression::factory( $options['pattern'] );
$schedule = new ActionScheduler_CronSchedule( $date, $cron );
break;

case 'recurring':
$date = as_get_datetime_object( $options['when'] );
$schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] );
break;

case 'single':
$date = as_get_datetime_object( $options['when'] );
$schedule = new ActionScheduler_SimpleSchedule( $date );
break;

default:
error_log( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." );
return 0;
}

$action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] );
$action->set_priority( $options['priority'] );

$action_id = 0;
try {
$action_id = $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action );
} catch ( Exception $e ) {
error_log(
sprintf(
/* translators: %1$s is the name of the hook to be enqueued, %2$s is the exception message. */
__( 'Caught exception while enqueuing action "%1$s": %2$s', 'action-scheduler' ),
$options['hook'],
$e->getMessage()
)
);
}
return $action_id;
}

/**
* Save action to database.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* Class ActionScheduler_Compatibility
*/
class ActionScheduler_Compatibility {

/**
* Converts a shorthand byte value to an integer byte value.
*
Expand Down Expand Up @@ -89,21 +88,18 @@ public static function raise_time_limit( $limit = 0 ) {
$limit = (int) $limit;
$max_execution_time = (int) ini_get( 'max_execution_time' );

/*
* If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed
* limit, there is no reason for us to make further changes (we never want to lower it).
*/
if (
0 === $max_execution_time
|| ( $max_execution_time >= $limit && $limit !== 0 )
) {
// If the max execution time is already set to zero (unlimited), there is no reason to make a further change.
if ( 0 === $max_execution_time ) {
return;
}

// Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit.
$raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time;

if ( function_exists( 'wc_set_time_limit' ) ) {
wc_set_time_limit( $limit );
wc_set_time_limit( $raise_by );
} elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
@set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
@set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ private static function human_interval( $interval, $periods_to_include = 2 ) {
*/
protected function get_recurrence( $action ) {
$schedule = $action->get_schedule();
if ( $schedule->is_recurring() ) {
if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) {
$recurrence = $schedule->get_recurrence();

if ( is_numeric( $recurrence ) ) {
Expand Down Expand Up @@ -471,7 +471,7 @@ protected function get_schedule_display_string( ActionScheduler_Schedule $schedu
return __( 'async', 'action-scheduler' );
}

if ( ! $schedule->get_date() ) {
if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) {
return '0000-00-00 00:00:00';
}

Expand Down Expand Up @@ -502,7 +502,20 @@ protected function get_schedule_display_string( ActionScheduler_Schedule $schedu
*/
protected function bulk_delete( array $ids, $ids_sql ) {
foreach ( $ids as $id ) {
$this->store->delete_action( $id );
try {
$this->store->delete_action( $id );
} catch ( Exception $e ) {
// A possible reason for an exception would include a scenario where the same action is deleted by a
// concurrent request.
error_log(
sprintf(
/* translators: 1: action ID 2: exception message. */
__( 'Action Scheduler was unable to delete action %1$d. Reason: %2$s', 'action-scheduler' ),
$id,
$e->getMessage()
)
);
}
}
}

Expand Down
Loading

0 comments on commit b396de2

Please sign in to comment.