Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update WP background processing and Action Scheduler #786

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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