Skip to content

Commit

Permalink
Fixes #6826 Move Action Scheduler to prevent PSR-4 warning (#6836)
Browse files Browse the repository at this point in the history
  • Loading branch information
remyperona authored Aug 9, 2024
1 parent c427503 commit b77fcfc
Show file tree
Hide file tree
Showing 93 changed files with 1,373 additions and 319 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
},
"extra": {
"installer-paths": {
"./inc/Dependencies/ActionScheduler/": ["woocommerce/action-scheduler"],
"dependencies/ActionScheduler/": ["woocommerce/action-scheduler"],
"vendor/{$vendor}/{$name}/": ["type:wordpress-plugin"]
},
"mozart": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Action Scheduler is a scalable, traceable job queue for background processing large sets of actions in WordPress. It's specially designed to be distributed in WordPress plugins.

Action Scheduler works by triggering an action hook to run at some time in the future. Each hook can be scheduled with unique data, to allow callbacks to perform operations on that data. The hook can also be scheduled to run on one or more occassions.
Action Scheduler works by triggering an action hook to run at some time in the future. Each hook can be scheduled with unique data, to allow callbacks to perform operations on that data. The hook can also be scheduled to run on one or more occasions.

Think of it like an extension to `do_action()` which adds the ability to delay and repeat a hook.

Expand Down
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.8.1
* License: GPLv3
* Requires at least: 6.2
* Tested up to: 6.5
* 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_8_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_8_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_8_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.8.1', 'action_scheduler_initialize_3_dot_8_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_8_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_8_dot_1(); // WRCS: DEFINED_VERSION.
do_action( 'action_scheduler_pre_theme_init' );
ActionScheduler_Versions::initialize_latest_version();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,87 @@
*** Changelog ***

= 3.8.1 - 2024-06-20 =
* Fix typos.
* Improve the messaging in our unidentified action exceptions.

= 3.8.0 - 2024-05-22 =
* Documentation - Fixed typos in perf.md.
* Update - We now require WordPress 6.3 or higher.
* Update - We now require PHP 7.0 or higher.

= 3.7.4 - 2024-04-05 =
* Give a clear description of how the $unique parameter works.
* Preserve the tab field if set.
* Tweak - WP 6.5 compatibility.

= 3.7.3 - 2024-03-20 =
* Do not iterate over all of GET when building form in list table.
* Fix a few issues reported by PCP (Plugin Check Plugin).
* Try to save actions as unique even when the store doesn't support it.
* Tweak - WP 6.4 compatibility.
* Update "Tested up to" tag to WordPress 6.5.
* update version in package-lock.json.

= 3.7.2 - 2024-02-14 =
* No longer user variables in `_n()` translation function.

= 3.7.1 - 2023-12-13 =
* 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_scheduled_date_gmt.
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
* 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 Expand Up @@ -43,7 +125,7 @@
* Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761
* Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768
* Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762
* Dev - Improve actions table indicies (props @glagonikas). #774 & #777
* Dev - Improve actions table indices (props @glagonikas). #774 & #777
* Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778
* Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779
* Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773
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 overridden 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 All @@ -253,7 +351,26 @@ protected function store( ActionScheduler_Action $action ) {
*/
protected function store_unique_action( ActionScheduler_Action $action ) {
$store = ActionScheduler_Store::instance();
return method_exists( $store, 'save_unique_action' ) ?
$store->save_unique_action( $action ) : $store->save_action( $action );
if ( method_exists( $store, 'save_unique_action' ) ) {
return $store->save_unique_action( $action );
} else {
/**
* Fallback to non-unique action if the store doesn't support unique actions.
* We try to save the action as unique, accepting that there might be a race condition.
* This is likely still better than giving up on unique actions entirely.
*/
$existing_action_id = (int) $store->find_action(
$action->get_hook(),
array(
'args' => $action->get_args(),
'status' => ActionScheduler_Store::STATUS_PENDING,
'group' => $action->get_group(),
)
);
if ( $existing_action_id > 0 ) {
return 0;
}
return $store->save_action( $action );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ protected function check_pastdue_actions() {

# Set thresholds.
$threshold_seconds = ( int ) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS );
$threshhold_min = ( int ) apply_filters( 'action_scheduler_pastdue_actions_min', 1 );
$threshold_min = ( int ) apply_filters( 'action_scheduler_pastdue_actions_min', 1 );

// Set fallback value for past-due actions count.
$num_pastdue_actions = 0;
Expand All @@ -162,7 +162,7 @@ protected function check_pastdue_actions() {
$query_args = array(
'date' => as_get_datetime_object( time() - $threshold_seconds ),
'status' => ActionScheduler_Store::STATUS_PENDING,
'per_page' => $threshhold_min,
'per_page' => $threshold_min,
);

# If no third-party preempted, run default check.
Expand All @@ -171,8 +171,8 @@ protected function check_pastdue_actions() {
$num_pastdue_actions = ( int ) $store->query_actions( $query_args, 'count' );

# Check if past-due actions count is greater than or equal to threshold.
$check = ( $num_pastdue_actions >= $threshhold_min );
$check = ( bool ) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshhold_min );
$check = ( $num_pastdue_actions >= $threshold_min );
$check = ( bool ) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshold_min );
}

# If check failed, set transient and abort.
Expand All @@ -192,8 +192,8 @@ protected function check_pastdue_actions() {
# Print notice.
echo '<div class="notice notice-warning"><p>';
printf(
// translators: 1) is the number of affected actions, 2) is a link to an admin screen.
_n(
// translators: 1) is the number of affected actions, 2) is a link to an admin screen.
'<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due action</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
'<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due actions</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
$num_pastdue_actions,
Expand Down Expand Up @@ -224,6 +224,7 @@ public function add_help_tabs() {
'id' => 'action_scheduler_about',
'title' => __( 'About', 'action-scheduler' ),
'content' =>
// translators: %s is the Action Scheduler version.
'<h2>' . sprintf( __( 'About Action Scheduler %s', 'action-scheduler' ), $as_version ) . '</h2>' .
'<p>' .
__( 'Action Scheduler is a scalable, traceable job queue for background processing large sets of actions. Action Scheduler works by triggering an action hook to run at some time in the future. Scheduled actions can also be scheduled to run on a recurring schedule.', 'action-scheduler' ) .
Expand Down
Loading

0 comments on commit b77fcfc

Please sign in to comment.