Changeset 2910869
- Timestamp:
- 05/10/2023 07:28:55 PM (3 years ago)
- Location:
- action-scheduler
- Files:
-
- 28 added
- 38 edited
- 1 copied
-
tags/3.6.0 (copied) (copied from action-scheduler/trunk)
-
tags/3.6.0/action-scheduler.php (modified) (5 diffs)
-
tags/3.6.0/changelog.txt (modified) (1 diff)
-
tags/3.6.0/classes/ActionScheduler_ActionFactory.php (modified) (3 diffs)
-
tags/3.6.0/classes/ActionScheduler_Compatibility.php (modified) (2 diffs)
-
tags/3.6.0/classes/ActionScheduler_ListTable.php (modified) (2 diffs)
-
tags/3.6.0/classes/ActionScheduler_QueueCleaner.php (modified) (3 diffs)
-
tags/3.6.0/classes/ActionScheduler_QueueRunner.php (modified) (1 diff)
-
tags/3.6.0/classes/WP_CLI/ActionScheduler_WPCLI_Clean_Command.php (added)
-
tags/3.6.0/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php (modified) (1 diff)
-
tags/3.6.0/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php (modified) (7 diffs)
-
tags/3.6.0/classes/abstracts/ActionScheduler.php (modified) (5 diffs)
-
tags/3.6.0/classes/abstracts/ActionScheduler_Abstract_ListTable.php (modified) (3 diffs)
-
tags/3.6.0/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php (modified) (2 diffs)
-
tags/3.6.0/classes/actions/ActionScheduler_Action.php (modified) (2 diffs)
-
tags/3.6.0/classes/data-stores/ActionScheduler_DBStore.php (modified) (13 diffs)
-
tags/3.6.0/classes/data-stores/ActionScheduler_wpPostStore.php (modified) (3 diffs)
-
tags/3.6.0/classes/migration/Runner.php (modified) (1 diff)
-
tags/3.6.0/classes/schema/ActionScheduler_StoreSchema.php (modified) (2 diffs)
-
tags/3.6.0/functions.php (modified) (8 diffs)
-
tags/3.6.0/readme.txt (modified) (2 diffs)
-
tags/3.6.0/vendor (added)
-
tags/3.6.0/vendor/autoload.php (added)
-
tags/3.6.0/vendor/composer (added)
-
tags/3.6.0/vendor/composer/ClassLoader.php (added)
-
tags/3.6.0/vendor/composer/InstalledVersions.php (added)
-
tags/3.6.0/vendor/composer/LICENSE (added)
-
tags/3.6.0/vendor/composer/autoload_classmap.php (added)
-
tags/3.6.0/vendor/composer/autoload_namespaces.php (added)
-
tags/3.6.0/vendor/composer/autoload_psr4.php (added)
-
tags/3.6.0/vendor/composer/autoload_real.php (added)
-
tags/3.6.0/vendor/composer/autoload_static.php (added)
-
tags/3.6.0/vendor/composer/installed.json (added)
-
tags/3.6.0/vendor/composer/installed.php (added)
-
trunk/action-scheduler.php (modified) (5 diffs)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/classes/ActionScheduler_ActionFactory.php (modified) (3 diffs)
-
trunk/classes/ActionScheduler_Compatibility.php (modified) (2 diffs)
-
trunk/classes/ActionScheduler_ListTable.php (modified) (2 diffs)
-
trunk/classes/ActionScheduler_QueueCleaner.php (modified) (3 diffs)
-
trunk/classes/ActionScheduler_QueueRunner.php (modified) (1 diff)
-
trunk/classes/WP_CLI/ActionScheduler_WPCLI_Clean_Command.php (added)
-
trunk/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php (modified) (1 diff)
-
trunk/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php (modified) (7 diffs)
-
trunk/classes/abstracts/ActionScheduler.php (modified) (5 diffs)
-
trunk/classes/abstracts/ActionScheduler_Abstract_ListTable.php (modified) (3 diffs)
-
trunk/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php (modified) (2 diffs)
-
trunk/classes/actions/ActionScheduler_Action.php (modified) (2 diffs)
-
trunk/classes/data-stores/ActionScheduler_DBStore.php (modified) (13 diffs)
-
trunk/classes/data-stores/ActionScheduler_wpPostStore.php (modified) (3 diffs)
-
trunk/classes/migration/Runner.php (modified) (1 diff)
-
trunk/classes/schema/ActionScheduler_StoreSchema.php (modified) (2 diffs)
-
trunk/functions.php (modified) (8 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/vendor (added)
-
trunk/vendor/autoload.php (added)
-
trunk/vendor/composer (added)
-
trunk/vendor/composer/ClassLoader.php (added)
-
trunk/vendor/composer/InstalledVersions.php (added)
-
trunk/vendor/composer/LICENSE (added)
-
trunk/vendor/composer/autoload_classmap.php (added)
-
trunk/vendor/composer/autoload_namespaces.php (added)
-
trunk/vendor/composer/autoload_psr4.php (added)
-
trunk/vendor/composer/autoload_real.php (added)
-
trunk/vendor/composer/autoload_static.php (added)
-
trunk/vendor/composer/installed.json (added)
-
trunk/vendor/composer/installed.php (added)
Legend:
- Unmodified
- Added
- Removed
-
action-scheduler/tags/3.6.0/action-scheduler.php
r2850016 r2910869 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com/ 8 * Version: 3. 5.48 * Version: 3.6.0 9 9 * License: GPLv3 10 10 * … … 27 27 */ 28 28 29 if ( ! function_exists( 'action_scheduler_register_3_dot_ 5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.29 if ( ! function_exists( 'action_scheduler_register_3_dot_6_dot_0' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION. 30 30 31 31 if ( ! class_exists( 'ActionScheduler_Versions', false ) ) { … … 34 34 } 35 35 36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_ 5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION.36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_6_dot_0', 0, 0 ); // WRCS: DEFINED_VERSION. 37 37 38 38 /** 39 39 * Registers this version of Action Scheduler. 40 40 */ 41 function action_scheduler_register_3_dot_ 5_dot_4() { // WRCS: DEFINED_VERSION.41 function action_scheduler_register_3_dot_6_dot_0() { // WRCS: DEFINED_VERSION. 42 42 $versions = ActionScheduler_Versions::instance(); 43 $versions->register( '3. 5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION.43 $versions->register( '3.6.0', 'action_scheduler_initialize_3_dot_6_dot_0' ); // WRCS: DEFINED_VERSION. 44 44 } 45 45 … … 47 47 * Initializes this version of Action Scheduler. 48 48 */ 49 function action_scheduler_initialize_3_dot_ 5_dot_4() { // WRCS: DEFINED_VERSION.49 function action_scheduler_initialize_3_dot_6_dot_0() { // WRCS: DEFINED_VERSION. 50 50 // A final safety check is required even here, because historic versions of Action Scheduler 51 51 // followed a different pattern (in some unusual cases, we could reach this point and the … … 59 59 // Support usage in themes - load this version if no plugin has loaded a version yet. 60 60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) { 61 action_scheduler_initialize_3_dot_ 5_dot_4(); // WRCS: DEFINED_VERSION.61 action_scheduler_initialize_3_dot_6_dot_0(); // WRCS: DEFINED_VERSION. 62 62 do_action( 'action_scheduler_pre_theme_init' ); 63 63 ActionScheduler_Versions::initialize_latest_version(); -
action-scheduler/tags/3.6.0/changelog.txt
r2850016 r2910869 1 1 *** Changelog *** 2 3 = 3.6.0 - 2023-05-10 = 4 * Add $unique parameter to function signatures. 5 * Add a cast-to-int for extra safety before forming new DateTime object. 6 * Add a hook allowing exceptions for consistently failing recurring actions. 7 * Add action priorities. 8 * Add init hook. 9 * Always raise the time limit. 10 * Bump minimatch from 3.0.4 to 3.0.8. 11 * Bump yaml from 2.2.1 to 2.2.2. 12 * Defensive coding relating to gaps in declared schedule types. 13 * Do not process an action if it cannot be set to `in-progress`. 14 * Filter view labels (status names) should be translatable | #919. 15 * Fix WPCLI progress messages. 16 * Improve data-store initialization flow. 17 * Improve error handling across all supported PHP versions. 18 * Improve logic for flushing the runtime cache. 19 * Support exclusion of multiple groups. 20 * Update lint-staged and Node/NPM requirements. 21 * add CLI clean command. 22 * add CLI exclude-group filter. 23 * exclude past-due from list table all filter count. 24 * throwing an exception if as_schedule_recurring_action interval param is not of type integer. 2 25 3 26 = 3.5.4 - 2023-01-17 = -
action-scheduler/tags/3.6.0/classes/ActionScheduler_ActionFactory.php
r2850016 r2910869 14 14 * @param ActionScheduler_Schedule $schedule The action's schedule. 15 15 * @param string $group A group to put the action in. 16 * @param int $priority The action priority. 16 17 * 17 18 * @return ActionScheduler_Action An instance of the stored action. 18 19 */ 19 20 public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) { 21 // The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with 22 // third-party subclasses created before this param was added. 23 $priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10; 20 24 21 25 switch ( $status ) { … … 37 41 38 42 $action = new $action_class( $hook, $args, $schedule, $group ); 43 $action->set_priority( $priority ); 39 44 40 45 /** 41 46 * Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group. 42 47 * 43 * @param ActionScheduler_Action $action The instantiated action.44 * @param string $hook The instantiated action's hook.45 * @param array $args The instantiated action's args.48 * @param ActionScheduler_Action $action The instantiated action. 49 * @param string $hook The instantiated action's hook. 50 * @param array $args The instantiated action's args. 46 51 * @param ActionScheduler_Schedule $schedule The instantiated action's schedule. 47 * @param string $group The instantiated action's group. 52 * @param string $group The instantiated action's group. 53 * @param int $priority The action priority. 48 54 */ 49 return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group );55 return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority ); 50 56 } 51 57 … … 230 236 $new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() ); 231 237 $new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() ); 238 $new_action->set_priority( $action->get_priority() ); 232 239 return $this->store( $new_action ); 240 } 241 242 /** 243 * Creates a scheduled action. 244 * 245 * This general purpose method can be used in place of specific methods such as async(), 246 * async_unique(), single() or single_unique(), etc. 247 * 248 * @internal Not intended for public use, should not be overriden by subclasses. 249 * @throws Exception May be thrown if invalid options are passed. 250 * 251 * @param array $options { 252 * Describes the action we wish to schedule. 253 * 254 * @type string $type Must be one of 'async', 'cron', 'recurring', or 'single'. 255 * @type string $hook The hook to be executed. 256 * @type array $arguments Arguments to be passed to the callback. 257 * @type string $group The action group. 258 * @type bool $unique If the action should be unique. 259 * @type int $when Timestamp. Indicates when the action, or first instance of the action in the case 260 * of recurring or cron actions, becomes due. 261 * @type int|string $pattern Recurrence pattern. This is either an interval in seconds for recurring actions 262 * or a cron expression for cron actions. 263 * @type int $priority Lower values means higher priority. Should be in the range 0-255. 264 * } 265 * 266 * @return int 267 */ 268 public function create( array $options = array() ) { 269 $defaults = array( 270 'type' => 'single', 271 'hook' => '', 272 'arguments' => array(), 273 'group' => '', 274 'unique' => false, 275 'when' => time(), 276 'pattern' => null, 277 'priority' => 10, 278 ); 279 280 $options = array_merge( $defaults, $options ); 281 282 // Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability 283 // to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions). 284 if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) { 285 $options['type'] = 'single'; 286 } 287 288 switch ( $options['type'] ) { 289 case 'async': 290 $schedule = new ActionScheduler_NullSchedule(); 291 break; 292 293 case 'cron': 294 $date = as_get_datetime_object( $options['when'] ); 295 $cron = CronExpression::factory( $options['pattern'] ); 296 $schedule = new ActionScheduler_CronSchedule( $date, $cron ); 297 break; 298 299 case 'recurring': 300 $date = as_get_datetime_object( $options['when'] ); 301 $schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] ); 302 break; 303 304 case 'single': 305 $date = as_get_datetime_object( $options['when'] ); 306 $schedule = new ActionScheduler_SimpleSchedule( $date ); 307 break; 308 309 default: 310 throw new Exception( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." ); 311 } 312 313 $action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] ); 314 $action->set_priority( $options['priority'] ); 315 return $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action ); 233 316 } 234 317 -
action-scheduler/tags/3.6.0/classes/ActionScheduler_Compatibility.php
r2622865 r2910869 5 5 */ 6 6 class ActionScheduler_Compatibility { 7 8 7 /** 9 8 * Converts a shorthand byte value to an integer byte value. … … 90 89 $max_execution_time = (int) ini_get( 'max_execution_time' ); 91 90 92 /* 93 * If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed 94 * limit, there is no reason for us to make further changes (we never want to lower it). 95 */ 96 if ( 97 0 === $max_execution_time 98 || ( $max_execution_time >= $limit && $limit !== 0 ) 99 ) { 91 // If the max execution time is already set to zero (unlimited), there is no reason to make a further change. 92 if ( 0 === $max_execution_time ) { 100 93 return; 101 94 } 102 95 96 // Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit. 97 $raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time; 98 103 99 if ( function_exists( 'wc_set_time_limit' ) ) { 104 wc_set_time_limit( $ limit);100 wc_set_time_limit( $raise_by ); 105 101 } 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 106 @set_time_limit( $ limit); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged102 @set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged 107 103 } 108 104 } -
action-scheduler/tags/3.6.0/classes/ActionScheduler_ListTable.php
r2775803 r2910869 253 253 protected function get_recurrence( $action ) { 254 254 $schedule = $action->get_schedule(); 255 if ( $schedule->is_recurring() ) {255 if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) { 256 256 $recurrence = $schedule->get_recurrence(); 257 257 … … 472 472 } 473 473 474 if ( ! $schedule->get_date() ) {474 if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) { 475 475 return '0000-00-00 00:00:00'; 476 476 } -
action-scheduler/tags/3.6.0/classes/ActionScheduler_QueueCleaner.php
r2542151 r2910869 20 20 21 21 /** 22 * @var string[] Default list of statuses purged by the cleaner process. 23 */ 24 private $default_statuses_to_purge = [ 25 ActionScheduler_Store::STATUS_COMPLETE, 26 ActionScheduler_Store::STATUS_CANCELED, 27 ]; 28 29 /** 22 30 * ActionScheduler_QueueCleaner constructor. 23 31 * … … 30 38 } 31 39 40 /** 41 * Default queue cleaner process used by queue runner. 42 * 43 * @return array 44 */ 32 45 public function delete_old_actions() { 46 /** 47 * Filter the minimum scheduled date age for action deletion. 48 * 49 * @param int $retention_period Minimum scheduled age in seconds of the actions to be deleted. 50 */ 33 51 $lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds ); 34 $cutoff = as_get_datetime_object($lifespan.' seconds ago'); 35 36 $statuses_to_purge = array( 37 ActionScheduler_Store::STATUS_COMPLETE, 38 ActionScheduler_Store::STATUS_CANCELED, 39 ); 40 52 53 try { 54 $cutoff = as_get_datetime_object( $lifespan . ' seconds ago' ); 55 } catch ( Exception $e ) { 56 _doing_it_wrong( 57 __METHOD__, 58 sprintf( 59 /* Translators: %s is the exception message. */ 60 esc_html__( 'It was not possible to determine a valid cut-off time: %s.', 'action-scheduler' ), 61 esc_html( $e->getMessage() ) 62 ), 63 '3.5.5' 64 ); 65 66 return array(); 67 } 68 69 70 /** 71 * Filter the statuses when cleaning the queue. 72 * 73 * @param string[] $default_statuses_to_purge Action statuses to clean. 74 */ 75 $statuses_to_purge = (array) apply_filters( 'action_scheduler_default_cleaner_statuses', $this->default_statuses_to_purge ); 76 77 return $this->clean_actions( $statuses_to_purge, $cutoff, $this->get_batch_size() ); 78 } 79 80 /** 81 * Delete selected actions limited by status and date. 82 * 83 * @param string[] $statuses_to_purge List of action statuses to purge. Defaults to canceled, complete. 84 * @param DateTime $cutoff_date Date limit for selecting actions. Defaults to 31 days ago. 85 * @param int|null $batch_size Maximum number of actions per status to delete. Defaults to 20. 86 * @param string $context Calling process context. Defaults to `old`. 87 * @return array Actions deleted. 88 */ 89 public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date, $batch_size = null, $context = 'old' ) { 90 $batch_size = $batch_size !== null ? $batch_size : $this->batch_size; 91 $cutoff = $cutoff_date !== null ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' ); 92 $lifespan = time() - $cutoff->getTimestamp(); 93 if ( empty( $statuses_to_purge ) ) { 94 $statuses_to_purge = $this->default_statuses_to_purge; 95 } 96 97 $deleted_actions = []; 41 98 foreach ( $statuses_to_purge as $status ) { 42 99 $actions_to_delete = $this->store->query_actions( array( … … 44 101 'modified' => $cutoff, 45 102 'modified_compare' => '<=', 46 'per_page' => $ this->get_batch_size(),103 'per_page' => $batch_size, 47 104 'orderby' => 'none', 48 105 ) ); 49 106 50 foreach ( $actions_to_delete as $action_id ) { 51 try { 52 $this->store->delete_action( $action_id ); 53 } catch ( Exception $e ) { 54 55 /** 56 * Notify 3rd party code of exceptions when deleting a completed action older than the retention period 57 * 58 * This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their 59 * actions. 60 * 61 * @since 2.0.0 62 * 63 * @param int $action_id The scheduled actions ID in the data store 64 * @param Exception $e The exception thrown when attempting to delete the action from the data store 65 * @param int $lifespan The retention period, in seconds, for old actions 66 * @param int $count_of_actions_to_delete The number of old actions being deleted in this batch 67 */ 68 do_action( 'action_scheduler_failed_old_action_deletion', $action_id, $e, $lifespan, count( $actions_to_delete ) ); 69 } 107 $deleted_actions = array_merge( $deleted_actions, $this->delete_actions( $actions_to_delete, $lifespan, $context ) ); 108 } 109 110 return $deleted_actions; 111 } 112 113 /** 114 * @param int[] $actions_to_delete List of action IDs to delete. 115 * @param int $lifespan Minimum scheduled age in seconds of the actions being deleted. 116 * @param string $context Context of the delete request. 117 * @return array Deleted action IDs. 118 */ 119 private function delete_actions( array $actions_to_delete, $lifespan = null, $context = 'old' ) { 120 $deleted_actions = []; 121 if ( $lifespan === null ) { 122 $lifespan = $this->month_in_seconds; 123 } 124 125 foreach ( $actions_to_delete as $action_id ) { 126 try { 127 $this->store->delete_action( $action_id ); 128 $deleted_actions[] = $action_id; 129 } catch ( Exception $e ) { 130 /** 131 * Notify 3rd party code of exceptions when deleting a completed action older than the retention period 132 * 133 * This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their 134 * actions. 135 * 136 * @param int $action_id The scheduled actions ID in the data store 137 * @param Exception $e The exception thrown when attempting to delete the action from the data store 138 * @param int $lifespan The retention period, in seconds, for old actions 139 * @param int $count_of_actions_to_delete The number of old actions being deleted in this batch 140 * @since 2.0.0 141 * 142 */ 143 do_action( "action_scheduler_failed_{$context}_action_deletion", $action_id, $e, $lifespan, count( $actions_to_delete ) ); 70 144 } 71 145 } 146 return $deleted_actions; 72 147 } 73 148 -
action-scheduler/tags/3.6.0/classes/ActionScheduler_QueueRunner.php
r2850016 r2910869 186 186 /* 187 187 * Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object 188 * cache, so we will always prefer this when it is available (but it was only introduced in WordPress 6.0). 188 * cache, so we will always prefer this method (as compared to calling wp_cache_flush()) when it is available. 189 * 190 * However, this function was only introduced in WordPress 6.0. Additionally, the preferred way of detecting if 191 * it is supported changed in WordPress 6.1 so we use two different methods to decide if we should utilize it. 189 192 */ 190 if ( function_exists( 'wp_cache_flush_runtime' ) ) { 193 $flushing_runtime_cache_explicitly_supported = function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_runtime' ); 194 $flushing_runtime_cache_implicitly_supported = ! function_exists( 'wp_cache_supports' ) && function_exists( 'wp_cache_flush_runtime' ); 195 196 if ( $flushing_runtime_cache_explicitly_supported || $flushing_runtime_cache_implicitly_supported ) { 191 197 wp_cache_flush_runtime(); 192 198 } elseif ( -
action-scheduler/tags/3.6.0/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php
r2340383 r2910869 91 91 $this->progress_bar = new ProgressBar( 92 92 /* translators: %d: amount of actions */ 93 sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), number_format_i18n( $count )),93 sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), $count ), 94 94 $count 95 95 ); -
action-scheduler/tags/3.6.0/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php
r2729833 r2910869 56 56 * : Only run actions from the specified group. Omitting this option runs actions from all groups. 57 57 * 58 * [--exclude-groups=<groups>] 59 * : Run actions from all groups except the specified group(s). Define multiple groups as a comma separated string (without spaces), e.g. '--group_a,group_b'. This option is ignored when `--group` is used. 60 * 58 61 * [--free-memory-on=<count>] 59 62 * : The number of actions to process between freeing memory. 0 disables freeing memory. Default 50. … … 73 76 public function run( $args, $assoc_args ) { 74 77 // Handle passed arguments. 75 $batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) ); 76 $batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) ); 77 $clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) ); 78 $hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) ); 79 $hooks = array_filter( array_map( 'trim', $hooks ) ); 80 $group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' ); 81 $free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 ); 82 $sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 ); 83 $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false ); 78 $batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) ); 79 $batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) ); 80 $clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) ); 81 $hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) ); 82 $hooks = array_filter( array_map( 'trim', $hooks ) ); 83 $group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' ); 84 $exclude_groups = \WP_CLI\Utils\get_flag_value( $assoc_args, 'exclude-groups', '' ); 85 $free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 ); 86 $sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 ); 87 $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false ); 84 88 85 89 ActionScheduler_DataController::set_free_ticks( $free_on ); … … 89 93 $actions_completed = 0; 90 94 $unlimited = $batches === 0; 95 if ( is_callable( [ ActionScheduler::store(), 'set_claim_filter' ] ) ) { 96 $exclude_groups = $this->parse_comma_separated_string( $exclude_groups ); 97 98 if ( ! empty( $exclude_groups ) ) { 99 ActionScheduler::store()->set_claim_filter('exclude-groups', $exclude_groups ); 100 } 101 } 91 102 92 103 try { … … 118 129 119 130 /** 131 * Converts a string of comma-separated values into an array of those same values. 132 * 133 * @param string $string The string of one or more comma separated values. 134 * 135 * @return array 136 */ 137 private function parse_comma_separated_string( $string ): array { 138 return array_filter( str_getcsv( $string ) ); 139 } 140 141 /** 120 142 * Print WP CLI message about how many actions are about to be processed. 121 143 * … … 127 149 WP_CLI::log( 128 150 sprintf( 129 /* translators: %d refers to how many scheduled ta ks were found to run */151 /* translators: %d refers to how many scheduled tasks were found to run */ 130 152 _n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ), 131 number_format_i18n( $total )153 $total 132 154 ) 133 155 ); … … 146 168 /* translators: %d refers to the total number of batches executed */ 147 169 _n( '%d batch executed.', '%d batches executed.', $batches_completed, 'action-scheduler' ), 148 number_format_i18n( $batches_completed )170 $batches_completed 149 171 ) 150 172 ); … … 180 202 WP_CLI::success( 181 203 sprintf( 182 /* translators: %d refers to the total number of task es completed */204 /* translators: %d refers to the total number of tasks completed */ 183 205 _n( '%d scheduled task completed.', '%d scheduled tasks completed.', $actions_completed, 'action-scheduler' ), 184 number_format_i18n( $actions_completed )206 $actions_completed 185 207 ) 186 208 ); -
action-scheduler/tags/3.6.0/classes/abstracts/ActionScheduler.php
r2729833 r2910869 154 154 add_action( 'init', array( $logger, 'init' ), 1, 0 ); 155 155 add_action( 'init', array( $runner, 'init' ), 1, 0 ); 156 157 add_action( 158 'init', 159 /** 160 * Runs after the active store's init() method has been called. 161 * 162 * It would probably be preferable to have $store->init() (or it's parent method) set this itself, 163 * once it has initialized, however that would cause problems in cases where a custom data store is in 164 * use and it has not yet been updated to follow that same logic. 165 */ 166 function () { 167 self::$data_store_initialized = true; 168 169 /** 170 * Fires when Action Scheduler is ready: it is safe to use the procedural API after this point. 171 * 172 * @since 3.5.5 173 */ 174 do_action( 'action_scheduler_init' ); 175 }, 176 1 177 ); 156 178 } else { 157 179 $admin_view->init(); … … 159 181 $logger->init(); 160 182 $runner->init(); 183 self::$data_store_initialized = true; 184 185 /** 186 * Fires when Action Scheduler is ready: it is safe to use the procedural API after this point. 187 * 188 * @since 3.5.5 189 */ 190 do_action( 'action_scheduler_init' ); 161 191 } 162 192 … … 167 197 if ( defined( 'WP_CLI' ) && WP_CLI ) { 168 198 WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' ); 199 WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Clean_Command' ); 169 200 if ( ! ActionScheduler_DataController::is_migration_complete() && Controller::instance()->allow_migration() ) { 170 201 $command = new Migration_Command(); … … 172 203 } 173 204 } 174 175 self::$data_store_initialized = true;176 205 177 206 /** … … 193 222 public static function is_initialized( $function_name = null ) { 194 223 if ( ! self::$data_store_initialized && ! empty( $function_name ) ) { 195 $message = sprintf( __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), esc_attr( $function_name ) ); 196 error_log( $message, E_WARNING ); 224 $message = sprintf( 225 /* translators: %s function name. */ 226 __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), 227 esc_attr( $function_name ) 228 ); 229 error_log( $message ); 197 230 } 198 231 -
action-scheduler/tags/3.6.0/classes/abstracts/ActionScheduler_Abstract_ListTable.php
r2775803 r2910869 674 674 // Helper to set 'all' filter when not set on status counts passed in. 675 675 if ( ! isset( $this->status_counts['all'] ) ) { 676 $this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts; 677 } 678 679 foreach ( $this->status_counts as $status_name => $count ) { 676 $all_count = array_sum( $this->status_counts ); 677 if ( isset( $this->status_counts['past-due'] ) ) { 678 $all_count -= $this->status_counts['past-due']; 679 } 680 $this->status_counts = array( 'all' => $all_count ) + $this->status_counts; 681 } 682 683 // Translated status labels. 684 $status_labels = ActionScheduler_Store::instance()->get_status_labels(); 685 $status_labels['all'] = _x( 'All', 'status labels', 'action-scheduler' ); 686 $status_labels['past-due'] = _x( 'Past-due', 'status labels', 'action-scheduler' ); 687 688 foreach ( $this->status_counts as $status_slug => $count ) { 680 689 681 690 if ( 0 === $count ) { … … 683 692 } 684 693 685 if ( $status_ name === $request_status || ( empty( $request_status ) && 'all' === $status_name) ) {694 if ( $status_slug === $request_status || ( empty( $request_status ) && 'all' === $status_slug ) ) { 686 695 $status_list_item = '<li class="%1$s"><a href="%2$s" class="current">%3$s</a> (%4$d)</li>'; 687 696 } else { … … 689 698 } 690 699 691 $status_filter_url = ( 'all' === $status_name ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_name ); 700 $status_name = isset( $status_labels[ $status_slug ] ) ? $status_labels[ $status_slug ] : ucfirst( $status_slug ); 701 $status_filter_url = ( 'all' === $status_slug ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_slug ); 692 702 $status_filter_url = remove_query_arg( array( 'paged', 's' ), $status_filter_url ); 693 $status_list_items[] = sprintf( $status_list_item, esc_attr( $status_ name ), esc_url( $status_filter_url ), esc_html( ucfirst( $status_name )), absint( $count ) );703 $status_list_items[] = sprintf( $status_list_item, esc_attr( $status_slug ), esc_url( $status_filter_url ), esc_html( $status_name ), absint( $count ) ); 694 704 } 695 705 -
action-scheduler/tags/3.6.0/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php
r2850016 r2910869 49 49 */ 50 50 public function process_action( $action_id, $context = '' ) { 51 // Temporarily override the error handler while we process the current action. 52 set_error_handler( 53 /** 54 * Temporary error handler which can catch errors and convert them into exceptions. This faciliates more 55 * robust error handling across all supported PHP versions. 56 * 57 * @throws Exception 58 * 59 * @param int $type Error level expressed as an integer. 60 * @param string $message Error message. 61 */ 62 function ( $type, $message ) { 63 throw new Exception( $message ); 64 }, 65 E_USER_ERROR | E_RECOVERABLE_ERROR 66 ); 67 68 /* 69 * The nested try/catch structure is required because we potentially need to convert thrown errors into 70 * exceptions (and an exception thrown from a catch block cannot be caught by a later catch block in the *same* 71 * structure). 72 */ 51 73 try { 52 $valid_action = false; 53 do_action( 'action_scheduler_before_execute', $action_id, $context ); 54 55 if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) { 56 do_action( 'action_scheduler_execution_ignored', $action_id, $context ); 57 return; 74 try { 75 $valid_action = false; 76 do_action( 'action_scheduler_before_execute', $action_id, $context ); 77 78 if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) { 79 do_action( 'action_scheduler_execution_ignored', $action_id, $context ); 80 return; 81 } 82 83 $valid_action = true; 84 do_action( 'action_scheduler_begin_execute', $action_id, $context ); 85 86 $action = $this->store->fetch_action( $action_id ); 87 $this->store->log_execution( $action_id ); 88 $action->execute(); 89 do_action( 'action_scheduler_after_execute', $action_id, $action, $context ); 90 $this->store->mark_complete( $action_id ); 91 } catch ( Throwable $e ) { 92 // Throwable is defined when executing under PHP 7.0 and up. We convert it to an exception, for 93 // compatibility with ActionScheduler_Logger. 94 throw new Exception( $e->getMessage(), $e->getCode(), $e->getPrevious() ); 58 95 } 59 60 $valid_action = true;61 do_action( 'action_scheduler_begin_execute', $action_id, $context );62 63 $action = $this->store->fetch_action( $action_id );64 $this->store->log_execution( $action_id );65 $action->execute();66 do_action( 'action_scheduler_after_execute', $action_id, $action, $context );67 $this->store->mark_complete( $action_id );68 96 } catch ( Exception $e ) { 69 if ( $valid_action ) { 70 $this->store->mark_failure( $action_id ); 71 do_action( 'action_scheduler_failed_execution', $action_id, $e, $context ); 72 } else { 73 do_action( 'action_scheduler_failed_validation', $action_id, $e, $context ); 74 } 97 // This catch block exists for compatibility with PHP 5.6. 98 $this->handle_action_error( $action_id, $e, $context, $valid_action ); 99 } finally { 100 restore_error_handler(); 75 101 } 76 102 77 103 if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) && $action->get_schedule()->is_recurring() ) { 78 104 $this->schedule_next_instance( $action, $action_id ); 105 } 106 } 107 108 /** 109 * Marks actions as either having failed execution or failed validation, as appropriate. 110 * 111 * @param int $action_id Action ID. 112 * @param Exception $e Exception instance. 113 * @param string $context Execution context. 114 * @param bool $valid_action If the action is valid. 115 * 116 * @return void 117 */ 118 private function handle_action_error( $action_id, $e, $context, $valid_action ) { 119 if ( $valid_action ) { 120 $this->store->mark_failure( $action_id ); 121 /** 122 * Runs when action execution fails. 123 * 124 * @param int $action_id Action ID. 125 * @param Exception $e Exception instance. 126 * @param string $context Execution context. 127 */ 128 do_action( 'action_scheduler_failed_execution', $action_id, $e, $context ); 129 } else { 130 /** 131 * Runs when action validation fails. 132 * 133 * @param int $action_id Action ID. 134 * @param Exception $e Exception instance. 135 * @param string $context Execution context. 136 */ 137 do_action( 'action_scheduler_failed_validation', $action_id, $e, $context ); 79 138 } 80 139 } … … 144 203 } 145 204 146 // Now let's fetch the first action (having the same hook) of *any status* ithin the same window.205 // Now let's fetch the first action (having the same hook) of *any status* within the same window. 147 206 unset( $query_args['status'] ); 148 207 $first_action_id_with_the_same_hook = $this->store->query_actions( $query_args ); 149 208 150 // If the IDs match, then actions for this hook must be consistently failing. 151 return $first_action_id_with_the_same_hook === $first_failing_action_id; 209 /** 210 * If a recurring action is assessed as consistently failing, it will not be rescheduled. This hook provides a 211 * way to observe and optionally override that assessment. 212 * 213 * @param bool $is_consistently_failing If the action is considered to be consistently failing. 214 * @param ActionScheduler_Action $action The action being assessed. 215 */ 216 return (bool) apply_filters( 217 'action_scheduler_recurring_action_is_consistently_failing', 218 $first_action_id_with_the_same_hook === $first_failing_action_id, 219 $action 220 ); 152 221 } 153 222 -
action-scheduler/tags/3.6.0/classes/actions/ActionScheduler_Action.php
r2775803 r2910869 10 10 protected $schedule = NULL; 11 11 protected $group = ''; 12 13 /** 14 * Priorities are conceptually similar to those used for regular WordPress actions. 15 * Like those, a lower priority takes precedence over a higher priority and the default 16 * is 10. 17 * 18 * Unlike regular WordPress actions, the priority of a scheduled action is strictly an 19 * integer and should be kept within the bounds 0-255 (anything outside the bounds will 20 * be brought back into the acceptable range). 21 * 22 * @var int 23 */ 24 protected $priority = 10; 12 25 13 26 public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) { … … 94 107 return FALSE; 95 108 } 109 110 /** 111 * Sets the priority of the action. 112 * 113 * @param int $priority Priority level (lower is higher priority). Should be in the range 0-255. 114 * 115 * @return void 116 */ 117 public function set_priority( $priority ) { 118 if ( $priority < 0 ) { 119 $priority = 0; 120 } elseif ( $priority > 255 ) { 121 $priority = 255; 122 } 123 124 $this->priority = (int) $priority; 125 } 126 127 /** 128 * Gets the action priority. 129 * 130 * @return int 131 */ 132 public function get_priority() { 133 return $this->priority; 134 } 96 135 } -
action-scheduler/tags/3.6.0/classes/data-stores/ActionScheduler_DBStore.php
r2850016 r2910869 26 26 protected static $max_index_length = 191; 27 27 28 /** @var array List of claim filters. */ 29 protected $claim_filters = [ 30 'group' => '', 31 'hooks' => '', 32 'exclude-groups' => '', 33 ]; 34 28 35 /** 29 36 * Initialize the data store … … 85 92 'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ), 86 93 'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize 87 'group_id' => $this->get_group_id( $action->get_group() ), 94 'group_id' => current( $this->get_group_ids( $action->get_group() ) ), 95 'priority' => $action->get_priority(), 88 96 ); 89 97 … … 173 181 ); 174 182 $pending_status_placeholders = implode( ', ', array_fill( 0, count( $pending_statuses ), '%s' ) ); 183 175 184 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pending_status_placeholders is hardcoded. 176 185 $where_clause = $wpdb->prepare( … … 243 252 * Get a group's ID based on its name/slug. 244 253 * 245 * @param string $slug The string name of a group. 246 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 247 * 248 * @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created. 249 */ 250 protected function get_group_id( $slug, $create_if_not_exists = true ) { 251 if ( empty( $slug ) ) { 252 return 0; 253 } 254 /** @var \wpdb $wpdb */ 255 global $wpdb; 256 $group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) ); 257 if ( empty( $group_id ) && $create_if_not_exists ) { 258 $group_id = $this->create_group( $slug ); 259 } 260 261 return $group_id; 254 * @param string|array $slugs The string name of a group, or names for several groups. 255 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 256 * 257 * @return array The group IDs, if they exist or were successfully created. May be empty. 258 */ 259 protected function get_group_ids( $slugs, $create_if_not_exists = true ) { 260 $slugs = (array) $slugs; 261 $group_ids = array(); 262 263 if ( empty( $slugs ) ) { 264 return array(); 265 } 266 267 /** @var \wpdb $wpdb */ 268 global $wpdb; 269 270 foreach ( $slugs as $slug ) { 271 $group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) ); 272 273 if ( empty( $group_id ) && $create_if_not_exists ) { 274 $group_id = $this->create_group( $slug ); 275 } 276 277 if ( $group_id ) { 278 $group_ids[] = $group_id; 279 } 280 } 281 282 return $group_ids; 262 283 } 263 284 … … 356 377 $group = $data->group ? $data->group : ''; 357 378 358 return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group );379 return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group, $data->priority ); 359 380 } 360 381 … … 798 819 799 820 /** 821 * Set a claim filter. 822 * 823 * @param string $filter_name Claim filter name. 824 * @param mixed $filter_values Values to filter. 825 * @return void 826 */ 827 public function set_claim_filter( $filter_name, $filter_values ) { 828 if ( isset( $this->claim_filters[ $filter_name ] ) ) { 829 $this->claim_filters[ $filter_name ] = $filter_values; 830 } 831 } 832 833 /** 834 * Get the claim filter value. 835 * 836 * @param string $filter_name Claim filter name. 837 * @return mixed 838 */ 839 public function get_claim_filter( $filter_name ) { 840 if ( isset( $this->claim_filters[ $filter_name ] ) ) { 841 return $this->claim_filters[ $filter_name ]; 842 } 843 844 return ''; 845 } 846 847 /** 800 848 * Mark actions claimed. 801 849 * … … 814 862 global $wpdb; 815 863 816 $now = as_get_datetime_object(); 817 $date = is_null( $before_date ) ? $now : clone $before_date; 818 864 $now = as_get_datetime_object(); 865 $date = is_null( $before_date ) ? $now : clone $before_date; 819 866 // can't use $wpdb->update() because of the <= condition. 820 867 $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s"; … … 825 872 ); 826 873 874 // Set claim filters. 875 if ( ! empty( $hooks ) ) { 876 $this->set_claim_filter( 'hooks', $hooks ); 877 } else { 878 $hooks = $this->get_claim_filter( 'hooks' ); 879 } 880 if ( ! empty( $group ) ) { 881 $this->set_claim_filter( 'group', $group ); 882 } else { 883 $group = $this->get_claim_filter( 'group' ); 884 } 885 827 886 $where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s'; 828 887 $params[] = $date->format( 'Y-m-d H:i:s' ); … … 835 894 } 836 895 896 $group_operator = 'IN'; 897 if ( empty( $group ) ) { 898 $group = $this->get_claim_filter( 'exclude-groups' ); 899 $group_operator = 'NOT IN'; 900 } 901 837 902 if ( ! empty( $group ) ) { 838 839 $group_id = $this->get_group_id( $group, false ); 840 841 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour. 842 if ( empty( $group_id ) ) { 843 /* translators: %s: group name */ 844 throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) ); 845 } 846 847 $where .= ' AND group_id = %d'; 848 $params[] = $group_id; 903 $group_ids = $this->get_group_ids( $group, false ); 904 905 // throw exception if no matching group(s) found, this matches ActionScheduler_wpPostStore's behaviour. 906 if ( empty( $group_ids ) ) { 907 throw new InvalidArgumentException( 908 sprintf( 909 /* translators: %s: group name(s) */ 910 _n( 911 'The group "%s" does not exist.', 912 'The groups "%s" do not exist.', 913 is_array( $group ) ? count( $group ) : 1, 914 'action-scheduler' 915 ), 916 $group 917 ) 918 ); 919 } 920 921 $id_list = implode( ',', array_map( 'intval', $group_ids ) ); 922 $where .= " AND group_id {$group_operator} ( $id_list )"; 849 923 } 850 924 … … 856 930 * @param string $order_by_sql 857 931 */ 858 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' );932 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC' ); 859 933 $params[] = $limit; 860 934 … … 913 987 914 988 $sql = $wpdb->prepare( 915 "SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ",989 "SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ORDER BY priority ASC", 916 990 $claim_id 917 991 ); … … 1006 1080 * Add execution message to action log. 1007 1081 * 1082 * @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress'). 1083 * 1008 1084 * @param int $action_id Action ID. 1009 1085 * … … 1016 1092 $sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d"; 1017 1093 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1018 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1094 1095 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1096 $status_updated = $wpdb->query( $sql ); 1097 1098 if ( ! $status_updated ) { 1099 throw new Exception( 1100 sprintf( 1101 /* translators: 1: action ID. 2: status slug. */ 1102 __( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ), 1103 $action_id, 1104 self::STATUS_RUNNING 1105 ) 1106 ); 1107 } 1019 1108 } 1020 1109 -
action-scheduler/tags/3.6.0/classes/data-stores/ActionScheduler_wpPostStore.php
r2775803 r2910869 937 937 * Log Execution. 938 938 * 939 * @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress'). 940 * 939 941 * @param string $action_id Action ID. 940 942 */ … … 948 950 949 951 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 950 $ wpdb->query(952 $status_updated = $wpdb->query( 951 953 $wpdb->prepare( 952 954 "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s", … … 958 960 ) 959 961 ); 962 963 if ( ! $status_updated ) { 964 throw new Exception( 965 sprintf( 966 /* translators: 1: action ID. 2: status slug. */ 967 __( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ), 968 $action_id, 969 self::STATUS_RUNNING 970 ) 971 ); 972 } 960 973 } 961 974 -
action-scheduler/tags/3.6.0/classes/migration/Runner.php
r2340383 r2910869 80 80 if ( $this->progress_bar ) { 81 81 /* translators: %d: amount of actions */ 82 $this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), number_format_i18n( $batch_size )) );82 $this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), $batch_size ) ); 83 83 $this->progress_bar->set_count( $batch_size ); 84 84 } -
action-scheduler/tags/3.6.0/classes/schema/ActionScheduler_StoreSchema.php
r2850016 r2910869 17 17 * @var int Increment this value to trigger a schema update. 18 18 */ 19 protected $schema_version = 6;19 protected $schema_version = 7; 20 20 21 21 public function __construct() { … … 50 50 scheduled_date_gmt datetime NULL default '{$default_date}', 51 51 scheduled_date_local datetime NULL default '{$default_date}', 52 priority tinyint unsigned NOT NULL default '10', 52 53 args varchar($max_index_length), 53 54 schedule longtext, -
action-scheduler/tags/3.6.0/functions.php
r2850016 r2910869 13 13 * @param string $group The group to assign this job to. 14 14 * @param bool $unique Whether the action should be unique. 15 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 15 16 * 16 17 * @return int The action ID. 17 18 */ 18 function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false ) {19 function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 19 20 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 20 21 return 0; … … 34 35 * @param array $args Action arguments. 35 36 * @param string $group Action group. 37 * @param int $priority Action priority. 36 38 */ 37 $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group );39 $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group, $priority ); 38 40 if ( null !== $pre ) { 39 41 return is_int( $pre ) ? $pre : 0; 40 42 } 41 43 42 return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique ); 44 return ActionScheduler::factory()->create( 45 array( 46 'type' => 'async', 47 'hook' => $hook, 48 'arguments' => $args, 49 'group' => $group, 50 'unique' => $unique, 51 'priority' => $priority, 52 ) 53 ); 43 54 } 44 55 … … 51 62 * @param string $group The group to assign this job to. 52 63 * @param bool $unique Whether the action should be unique. 64 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 53 65 * 54 66 * @return int The action ID. 55 67 */ 56 function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false ) {68 function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 57 69 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 58 70 return 0; … … 73 85 * @param array $args Action arguments. 74 86 * @param string $group Action group. 87 * @param int $priorities Action priority. 75 88 */ 76 $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group );89 $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority ); 77 90 if ( null !== $pre ) { 78 91 return is_int( $pre ) ? $pre : 0; 79 92 } 80 93 81 return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique ); 94 return ActionScheduler::factory()->create( 95 array( 96 'type' => 'single', 97 'hook' => $hook, 98 'arguments' => $args, 99 'when' => $timestamp, 100 'group' => $group, 101 'unique' => $unique, 102 'priority' => $priority, 103 ) 104 ); 82 105 } 83 106 … … 91 114 * @param string $group The group to assign this job to. 92 115 * @param bool $unique Whether the action should be unique. 116 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 93 117 * 94 118 * @return int The action ID. 95 119 */ 96 function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false ) { 97 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 120 function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 121 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 122 return 0; 123 } 124 125 $interval = (int) $interval_in_seconds; 126 127 // We expect an integer and allow it to be passed using float and string types, but otherwise 128 // should reject unexpected values. 129 if ( ! is_numeric( $interval_in_seconds ) || $interval_in_seconds != $interval ) { 130 _doing_it_wrong( 131 __METHOD__, 132 sprintf( 133 /* translators: 1: provided value 2: provided type. */ 134 esc_html__( 'An integer was expected but "%1$s" (%2$s) was received.', 'action-scheduler' ), 135 esc_html( $interval_in_seconds ), 136 esc_html( gettype( $interval_in_seconds ) ) 137 ), 138 '3.6.0' 139 ); 140 98 141 return 0; 99 142 } … … 114 157 * @param array $args Action arguments. 115 158 * @param string $group Action group. 159 * @param int $priority Action priority. 116 160 */ 117 $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group );161 $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority ); 118 162 if ( null !== $pre ) { 119 163 return is_int( $pre ) ? $pre : 0; 120 164 } 121 165 122 return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique ); 166 return ActionScheduler::factory()->create( 167 array( 168 'type' => 'recurring', 169 'hook' => $hook, 170 'arguments' => $args, 171 'when' => $timestamp, 172 'pattern' => $interval_in_seconds, 173 'group' => $group, 174 'unique' => $unique, 175 'priority' => $priority, 176 ) 177 ); 123 178 } 124 179 … … 144 199 * @param string $group The group to assign this job to. 145 200 * @param bool $unique Whether the action should be unique. 201 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 146 202 * 147 203 * @return int The action ID. 148 204 */ 149 function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false ) {205 function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 150 206 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 151 207 return 0; … … 167 223 * @param array $args Action arguments. 168 224 * @param string $group Action group. 225 * @param int $priority Action priority. 169 226 */ 170 $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group );227 $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority ); 171 228 if ( null !== $pre ) { 172 229 return is_int( $pre ) ? $pre : 0; 173 230 } 174 231 175 return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique ); 232 return ActionScheduler::factory()->create( 233 array( 234 'type' => 'cron', 235 'hook' => $hook, 236 'arguments' => $args, 237 'when' => $timestamp, 238 'pattern' => $schedule, 239 'group' => $group, 240 'unique' => $unique, 241 'priority' => $priority, 242 ) 243 ); 176 244 } 177 245 -
action-scheduler/tags/3.6.0/readme.txt
r2850016 r2910869 4 4 Requires at least: 5.2 5 5 Tested up to: 6.0 6 Stable tag: 3. 5.46 Stable tag: 3.6.0 7 7 License: GPLv3 8 8 Requires PHP: 5.6 … … 47 47 48 48 == Changelog == 49 50 = 3.6.0 - 2023-05-10 = 51 * Add $unique parameter to function signatures. 52 * Add a cast-to-int for extra safety before forming new DateTime object. 53 * Add a hook allowing exceptions for consistently failing recurring actions. 54 * Add action priorities. 55 * Add init hook. 56 * Always raise the time limit. 57 * Bump minimatch from 3.0.4 to 3.0.8. 58 * Bump yaml from 2.2.1 to 2.2.2. 59 * Defensive coding relating to gaps in declared schedule types. 60 * Do not process an action if it cannot be set to `in-progress`. 61 * Filter view labels (status names) should be translatable | #919. 62 * Fix WPCLI progress messages. 63 * Improve data-store initialization flow. 64 * Improve error handling across all supported PHP versions. 65 * Improve logic for flushing the runtime cache. 66 * Support exclusion of multiple groups. 67 * Update lint-staged and Node/NPM requirements. 68 * add CLI clean command. 69 * add CLI exclude-group filter. 70 * exclude past-due from list table all filter count. 71 * throwing an exception if as_schedule_recurring_action interval param is not of type integer. 49 72 50 73 = 3.5.4 - 2023-01-17 = -
action-scheduler/trunk/action-scheduler.php
r2850016 r2910869 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com/ 8 * Version: 3. 5.48 * Version: 3.6.0 9 9 * License: GPLv3 10 10 * … … 27 27 */ 28 28 29 if ( ! function_exists( 'action_scheduler_register_3_dot_ 5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.29 if ( ! function_exists( 'action_scheduler_register_3_dot_6_dot_0' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION. 30 30 31 31 if ( ! class_exists( 'ActionScheduler_Versions', false ) ) { … … 34 34 } 35 35 36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_ 5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION.36 add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_6_dot_0', 0, 0 ); // WRCS: DEFINED_VERSION. 37 37 38 38 /** 39 39 * Registers this version of Action Scheduler. 40 40 */ 41 function action_scheduler_register_3_dot_ 5_dot_4() { // WRCS: DEFINED_VERSION.41 function action_scheduler_register_3_dot_6_dot_0() { // WRCS: DEFINED_VERSION. 42 42 $versions = ActionScheduler_Versions::instance(); 43 $versions->register( '3. 5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION.43 $versions->register( '3.6.0', 'action_scheduler_initialize_3_dot_6_dot_0' ); // WRCS: DEFINED_VERSION. 44 44 } 45 45 … … 47 47 * Initializes this version of Action Scheduler. 48 48 */ 49 function action_scheduler_initialize_3_dot_ 5_dot_4() { // WRCS: DEFINED_VERSION.49 function action_scheduler_initialize_3_dot_6_dot_0() { // WRCS: DEFINED_VERSION. 50 50 // A final safety check is required even here, because historic versions of Action Scheduler 51 51 // followed a different pattern (in some unusual cases, we could reach this point and the … … 59 59 // Support usage in themes - load this version if no plugin has loaded a version yet. 60 60 if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) { 61 action_scheduler_initialize_3_dot_ 5_dot_4(); // WRCS: DEFINED_VERSION.61 action_scheduler_initialize_3_dot_6_dot_0(); // WRCS: DEFINED_VERSION. 62 62 do_action( 'action_scheduler_pre_theme_init' ); 63 63 ActionScheduler_Versions::initialize_latest_version(); -
action-scheduler/trunk/changelog.txt
r2850016 r2910869 1 1 *** Changelog *** 2 3 = 3.6.0 - 2023-05-10 = 4 * Add $unique parameter to function signatures. 5 * Add a cast-to-int for extra safety before forming new DateTime object. 6 * Add a hook allowing exceptions for consistently failing recurring actions. 7 * Add action priorities. 8 * Add init hook. 9 * Always raise the time limit. 10 * Bump minimatch from 3.0.4 to 3.0.8. 11 * Bump yaml from 2.2.1 to 2.2.2. 12 * Defensive coding relating to gaps in declared schedule types. 13 * Do not process an action if it cannot be set to `in-progress`. 14 * Filter view labels (status names) should be translatable | #919. 15 * Fix WPCLI progress messages. 16 * Improve data-store initialization flow. 17 * Improve error handling across all supported PHP versions. 18 * Improve logic for flushing the runtime cache. 19 * Support exclusion of multiple groups. 20 * Update lint-staged and Node/NPM requirements. 21 * add CLI clean command. 22 * add CLI exclude-group filter. 23 * exclude past-due from list table all filter count. 24 * throwing an exception if as_schedule_recurring_action interval param is not of type integer. 2 25 3 26 = 3.5.4 - 2023-01-17 = -
action-scheduler/trunk/classes/ActionScheduler_ActionFactory.php
r2850016 r2910869 14 14 * @param ActionScheduler_Schedule $schedule The action's schedule. 15 15 * @param string $group A group to put the action in. 16 * @param int $priority The action priority. 16 17 * 17 18 * @return ActionScheduler_Action An instance of the stored action. 18 19 */ 19 20 public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) { 21 // The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with 22 // third-party subclasses created before this param was added. 23 $priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10; 20 24 21 25 switch ( $status ) { … … 37 41 38 42 $action = new $action_class( $hook, $args, $schedule, $group ); 43 $action->set_priority( $priority ); 39 44 40 45 /** 41 46 * Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group. 42 47 * 43 * @param ActionScheduler_Action $action The instantiated action.44 * @param string $hook The instantiated action's hook.45 * @param array $args The instantiated action's args.48 * @param ActionScheduler_Action $action The instantiated action. 49 * @param string $hook The instantiated action's hook. 50 * @param array $args The instantiated action's args. 46 51 * @param ActionScheduler_Schedule $schedule The instantiated action's schedule. 47 * @param string $group The instantiated action's group. 52 * @param string $group The instantiated action's group. 53 * @param int $priority The action priority. 48 54 */ 49 return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group );55 return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority ); 50 56 } 51 57 … … 230 236 $new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() ); 231 237 $new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() ); 238 $new_action->set_priority( $action->get_priority() ); 232 239 return $this->store( $new_action ); 240 } 241 242 /** 243 * Creates a scheduled action. 244 * 245 * This general purpose method can be used in place of specific methods such as async(), 246 * async_unique(), single() or single_unique(), etc. 247 * 248 * @internal Not intended for public use, should not be overriden by subclasses. 249 * @throws Exception May be thrown if invalid options are passed. 250 * 251 * @param array $options { 252 * Describes the action we wish to schedule. 253 * 254 * @type string $type Must be one of 'async', 'cron', 'recurring', or 'single'. 255 * @type string $hook The hook to be executed. 256 * @type array $arguments Arguments to be passed to the callback. 257 * @type string $group The action group. 258 * @type bool $unique If the action should be unique. 259 * @type int $when Timestamp. Indicates when the action, or first instance of the action in the case 260 * of recurring or cron actions, becomes due. 261 * @type int|string $pattern Recurrence pattern. This is either an interval in seconds for recurring actions 262 * or a cron expression for cron actions. 263 * @type int $priority Lower values means higher priority. Should be in the range 0-255. 264 * } 265 * 266 * @return int 267 */ 268 public function create( array $options = array() ) { 269 $defaults = array( 270 'type' => 'single', 271 'hook' => '', 272 'arguments' => array(), 273 'group' => '', 274 'unique' => false, 275 'when' => time(), 276 'pattern' => null, 277 'priority' => 10, 278 ); 279 280 $options = array_merge( $defaults, $options ); 281 282 // Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability 283 // to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions). 284 if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) { 285 $options['type'] = 'single'; 286 } 287 288 switch ( $options['type'] ) { 289 case 'async': 290 $schedule = new ActionScheduler_NullSchedule(); 291 break; 292 293 case 'cron': 294 $date = as_get_datetime_object( $options['when'] ); 295 $cron = CronExpression::factory( $options['pattern'] ); 296 $schedule = new ActionScheduler_CronSchedule( $date, $cron ); 297 break; 298 299 case 'recurring': 300 $date = as_get_datetime_object( $options['when'] ); 301 $schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] ); 302 break; 303 304 case 'single': 305 $date = as_get_datetime_object( $options['when'] ); 306 $schedule = new ActionScheduler_SimpleSchedule( $date ); 307 break; 308 309 default: 310 throw new Exception( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." ); 311 } 312 313 $action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] ); 314 $action->set_priority( $options['priority'] ); 315 return $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action ); 233 316 } 234 317 -
action-scheduler/trunk/classes/ActionScheduler_Compatibility.php
r2622865 r2910869 5 5 */ 6 6 class ActionScheduler_Compatibility { 7 8 7 /** 9 8 * Converts a shorthand byte value to an integer byte value. … … 90 89 $max_execution_time = (int) ini_get( 'max_execution_time' ); 91 90 92 /* 93 * If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed 94 * limit, there is no reason for us to make further changes (we never want to lower it). 95 */ 96 if ( 97 0 === $max_execution_time 98 || ( $max_execution_time >= $limit && $limit !== 0 ) 99 ) { 91 // If the max execution time is already set to zero (unlimited), there is no reason to make a further change. 92 if ( 0 === $max_execution_time ) { 100 93 return; 101 94 } 102 95 96 // Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit. 97 $raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time; 98 103 99 if ( function_exists( 'wc_set_time_limit' ) ) { 104 wc_set_time_limit( $ limit);100 wc_set_time_limit( $raise_by ); 105 101 } 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 106 @set_time_limit( $ limit); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged102 @set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged 107 103 } 108 104 } -
action-scheduler/trunk/classes/ActionScheduler_ListTable.php
r2775803 r2910869 253 253 protected function get_recurrence( $action ) { 254 254 $schedule = $action->get_schedule(); 255 if ( $schedule->is_recurring() ) {255 if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) { 256 256 $recurrence = $schedule->get_recurrence(); 257 257 … … 472 472 } 473 473 474 if ( ! $schedule->get_date() ) {474 if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) { 475 475 return '0000-00-00 00:00:00'; 476 476 } -
action-scheduler/trunk/classes/ActionScheduler_QueueCleaner.php
r2542151 r2910869 20 20 21 21 /** 22 * @var string[] Default list of statuses purged by the cleaner process. 23 */ 24 private $default_statuses_to_purge = [ 25 ActionScheduler_Store::STATUS_COMPLETE, 26 ActionScheduler_Store::STATUS_CANCELED, 27 ]; 28 29 /** 22 30 * ActionScheduler_QueueCleaner constructor. 23 31 * … … 30 38 } 31 39 40 /** 41 * Default queue cleaner process used by queue runner. 42 * 43 * @return array 44 */ 32 45 public function delete_old_actions() { 46 /** 47 * Filter the minimum scheduled date age for action deletion. 48 * 49 * @param int $retention_period Minimum scheduled age in seconds of the actions to be deleted. 50 */ 33 51 $lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds ); 34 $cutoff = as_get_datetime_object($lifespan.' seconds ago'); 35 36 $statuses_to_purge = array( 37 ActionScheduler_Store::STATUS_COMPLETE, 38 ActionScheduler_Store::STATUS_CANCELED, 39 ); 40 52 53 try { 54 $cutoff = as_get_datetime_object( $lifespan . ' seconds ago' ); 55 } catch ( Exception $e ) { 56 _doing_it_wrong( 57 __METHOD__, 58 sprintf( 59 /* Translators: %s is the exception message. */ 60 esc_html__( 'It was not possible to determine a valid cut-off time: %s.', 'action-scheduler' ), 61 esc_html( $e->getMessage() ) 62 ), 63 '3.5.5' 64 ); 65 66 return array(); 67 } 68 69 70 /** 71 * Filter the statuses when cleaning the queue. 72 * 73 * @param string[] $default_statuses_to_purge Action statuses to clean. 74 */ 75 $statuses_to_purge = (array) apply_filters( 'action_scheduler_default_cleaner_statuses', $this->default_statuses_to_purge ); 76 77 return $this->clean_actions( $statuses_to_purge, $cutoff, $this->get_batch_size() ); 78 } 79 80 /** 81 * Delete selected actions limited by status and date. 82 * 83 * @param string[] $statuses_to_purge List of action statuses to purge. Defaults to canceled, complete. 84 * @param DateTime $cutoff_date Date limit for selecting actions. Defaults to 31 days ago. 85 * @param int|null $batch_size Maximum number of actions per status to delete. Defaults to 20. 86 * @param string $context Calling process context. Defaults to `old`. 87 * @return array Actions deleted. 88 */ 89 public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date, $batch_size = null, $context = 'old' ) { 90 $batch_size = $batch_size !== null ? $batch_size : $this->batch_size; 91 $cutoff = $cutoff_date !== null ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' ); 92 $lifespan = time() - $cutoff->getTimestamp(); 93 if ( empty( $statuses_to_purge ) ) { 94 $statuses_to_purge = $this->default_statuses_to_purge; 95 } 96 97 $deleted_actions = []; 41 98 foreach ( $statuses_to_purge as $status ) { 42 99 $actions_to_delete = $this->store->query_actions( array( … … 44 101 'modified' => $cutoff, 45 102 'modified_compare' => '<=', 46 'per_page' => $ this->get_batch_size(),103 'per_page' => $batch_size, 47 104 'orderby' => 'none', 48 105 ) ); 49 106 50 foreach ( $actions_to_delete as $action_id ) { 51 try { 52 $this->store->delete_action( $action_id ); 53 } catch ( Exception $e ) { 54 55 /** 56 * Notify 3rd party code of exceptions when deleting a completed action older than the retention period 57 * 58 * This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their 59 * actions. 60 * 61 * @since 2.0.0 62 * 63 * @param int $action_id The scheduled actions ID in the data store 64 * @param Exception $e The exception thrown when attempting to delete the action from the data store 65 * @param int $lifespan The retention period, in seconds, for old actions 66 * @param int $count_of_actions_to_delete The number of old actions being deleted in this batch 67 */ 68 do_action( 'action_scheduler_failed_old_action_deletion', $action_id, $e, $lifespan, count( $actions_to_delete ) ); 69 } 107 $deleted_actions = array_merge( $deleted_actions, $this->delete_actions( $actions_to_delete, $lifespan, $context ) ); 108 } 109 110 return $deleted_actions; 111 } 112 113 /** 114 * @param int[] $actions_to_delete List of action IDs to delete. 115 * @param int $lifespan Minimum scheduled age in seconds of the actions being deleted. 116 * @param string $context Context of the delete request. 117 * @return array Deleted action IDs. 118 */ 119 private function delete_actions( array $actions_to_delete, $lifespan = null, $context = 'old' ) { 120 $deleted_actions = []; 121 if ( $lifespan === null ) { 122 $lifespan = $this->month_in_seconds; 123 } 124 125 foreach ( $actions_to_delete as $action_id ) { 126 try { 127 $this->store->delete_action( $action_id ); 128 $deleted_actions[] = $action_id; 129 } catch ( Exception $e ) { 130 /** 131 * Notify 3rd party code of exceptions when deleting a completed action older than the retention period 132 * 133 * This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their 134 * actions. 135 * 136 * @param int $action_id The scheduled actions ID in the data store 137 * @param Exception $e The exception thrown when attempting to delete the action from the data store 138 * @param int $lifespan The retention period, in seconds, for old actions 139 * @param int $count_of_actions_to_delete The number of old actions being deleted in this batch 140 * @since 2.0.0 141 * 142 */ 143 do_action( "action_scheduler_failed_{$context}_action_deletion", $action_id, $e, $lifespan, count( $actions_to_delete ) ); 70 144 } 71 145 } 146 return $deleted_actions; 72 147 } 73 148 -
action-scheduler/trunk/classes/ActionScheduler_QueueRunner.php
r2850016 r2910869 186 186 /* 187 187 * Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object 188 * cache, so we will always prefer this when it is available (but it was only introduced in WordPress 6.0). 188 * cache, so we will always prefer this method (as compared to calling wp_cache_flush()) when it is available. 189 * 190 * However, this function was only introduced in WordPress 6.0. Additionally, the preferred way of detecting if 191 * it is supported changed in WordPress 6.1 so we use two different methods to decide if we should utilize it. 189 192 */ 190 if ( function_exists( 'wp_cache_flush_runtime' ) ) { 193 $flushing_runtime_cache_explicitly_supported = function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_runtime' ); 194 $flushing_runtime_cache_implicitly_supported = ! function_exists( 'wp_cache_supports' ) && function_exists( 'wp_cache_flush_runtime' ); 195 196 if ( $flushing_runtime_cache_explicitly_supported || $flushing_runtime_cache_implicitly_supported ) { 191 197 wp_cache_flush_runtime(); 192 198 } elseif ( -
action-scheduler/trunk/classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php
r2340383 r2910869 91 91 $this->progress_bar = new ProgressBar( 92 92 /* translators: %d: amount of actions */ 93 sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), number_format_i18n( $count )),93 sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), $count ), 94 94 $count 95 95 ); -
action-scheduler/trunk/classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php
r2729833 r2910869 56 56 * : Only run actions from the specified group. Omitting this option runs actions from all groups. 57 57 * 58 * [--exclude-groups=<groups>] 59 * : Run actions from all groups except the specified group(s). Define multiple groups as a comma separated string (without spaces), e.g. '--group_a,group_b'. This option is ignored when `--group` is used. 60 * 58 61 * [--free-memory-on=<count>] 59 62 * : The number of actions to process between freeing memory. 0 disables freeing memory. Default 50. … … 73 76 public function run( $args, $assoc_args ) { 74 77 // Handle passed arguments. 75 $batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) ); 76 $batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) ); 77 $clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) ); 78 $hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) ); 79 $hooks = array_filter( array_map( 'trim', $hooks ) ); 80 $group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' ); 81 $free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 ); 82 $sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 ); 83 $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false ); 78 $batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) ); 79 $batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) ); 80 $clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) ); 81 $hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) ); 82 $hooks = array_filter( array_map( 'trim', $hooks ) ); 83 $group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' ); 84 $exclude_groups = \WP_CLI\Utils\get_flag_value( $assoc_args, 'exclude-groups', '' ); 85 $free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 ); 86 $sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 ); 87 $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false ); 84 88 85 89 ActionScheduler_DataController::set_free_ticks( $free_on ); … … 89 93 $actions_completed = 0; 90 94 $unlimited = $batches === 0; 95 if ( is_callable( [ ActionScheduler::store(), 'set_claim_filter' ] ) ) { 96 $exclude_groups = $this->parse_comma_separated_string( $exclude_groups ); 97 98 if ( ! empty( $exclude_groups ) ) { 99 ActionScheduler::store()->set_claim_filter('exclude-groups', $exclude_groups ); 100 } 101 } 91 102 92 103 try { … … 118 129 119 130 /** 131 * Converts a string of comma-separated values into an array of those same values. 132 * 133 * @param string $string The string of one or more comma separated values. 134 * 135 * @return array 136 */ 137 private function parse_comma_separated_string( $string ): array { 138 return array_filter( str_getcsv( $string ) ); 139 } 140 141 /** 120 142 * Print WP CLI message about how many actions are about to be processed. 121 143 * … … 127 149 WP_CLI::log( 128 150 sprintf( 129 /* translators: %d refers to how many scheduled ta ks were found to run */151 /* translators: %d refers to how many scheduled tasks were found to run */ 130 152 _n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ), 131 number_format_i18n( $total )153 $total 132 154 ) 133 155 ); … … 146 168 /* translators: %d refers to the total number of batches executed */ 147 169 _n( '%d batch executed.', '%d batches executed.', $batches_completed, 'action-scheduler' ), 148 number_format_i18n( $batches_completed )170 $batches_completed 149 171 ) 150 172 ); … … 180 202 WP_CLI::success( 181 203 sprintf( 182 /* translators: %d refers to the total number of task es completed */204 /* translators: %d refers to the total number of tasks completed */ 183 205 _n( '%d scheduled task completed.', '%d scheduled tasks completed.', $actions_completed, 'action-scheduler' ), 184 number_format_i18n( $actions_completed )206 $actions_completed 185 207 ) 186 208 ); -
action-scheduler/trunk/classes/abstracts/ActionScheduler.php
r2729833 r2910869 154 154 add_action( 'init', array( $logger, 'init' ), 1, 0 ); 155 155 add_action( 'init', array( $runner, 'init' ), 1, 0 ); 156 157 add_action( 158 'init', 159 /** 160 * Runs after the active store's init() method has been called. 161 * 162 * It would probably be preferable to have $store->init() (or it's parent method) set this itself, 163 * once it has initialized, however that would cause problems in cases where a custom data store is in 164 * use and it has not yet been updated to follow that same logic. 165 */ 166 function () { 167 self::$data_store_initialized = true; 168 169 /** 170 * Fires when Action Scheduler is ready: it is safe to use the procedural API after this point. 171 * 172 * @since 3.5.5 173 */ 174 do_action( 'action_scheduler_init' ); 175 }, 176 1 177 ); 156 178 } else { 157 179 $admin_view->init(); … … 159 181 $logger->init(); 160 182 $runner->init(); 183 self::$data_store_initialized = true; 184 185 /** 186 * Fires when Action Scheduler is ready: it is safe to use the procedural API after this point. 187 * 188 * @since 3.5.5 189 */ 190 do_action( 'action_scheduler_init' ); 161 191 } 162 192 … … 167 197 if ( defined( 'WP_CLI' ) && WP_CLI ) { 168 198 WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' ); 199 WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Clean_Command' ); 169 200 if ( ! ActionScheduler_DataController::is_migration_complete() && Controller::instance()->allow_migration() ) { 170 201 $command = new Migration_Command(); … … 172 203 } 173 204 } 174 175 self::$data_store_initialized = true;176 205 177 206 /** … … 193 222 public static function is_initialized( $function_name = null ) { 194 223 if ( ! self::$data_store_initialized && ! empty( $function_name ) ) { 195 $message = sprintf( __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), esc_attr( $function_name ) ); 196 error_log( $message, E_WARNING ); 224 $message = sprintf( 225 /* translators: %s function name. */ 226 __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), 227 esc_attr( $function_name ) 228 ); 229 error_log( $message ); 197 230 } 198 231 -
action-scheduler/trunk/classes/abstracts/ActionScheduler_Abstract_ListTable.php
r2775803 r2910869 674 674 // Helper to set 'all' filter when not set on status counts passed in. 675 675 if ( ! isset( $this->status_counts['all'] ) ) { 676 $this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts; 677 } 678 679 foreach ( $this->status_counts as $status_name => $count ) { 676 $all_count = array_sum( $this->status_counts ); 677 if ( isset( $this->status_counts['past-due'] ) ) { 678 $all_count -= $this->status_counts['past-due']; 679 } 680 $this->status_counts = array( 'all' => $all_count ) + $this->status_counts; 681 } 682 683 // Translated status labels. 684 $status_labels = ActionScheduler_Store::instance()->get_status_labels(); 685 $status_labels['all'] = _x( 'All', 'status labels', 'action-scheduler' ); 686 $status_labels['past-due'] = _x( 'Past-due', 'status labels', 'action-scheduler' ); 687 688 foreach ( $this->status_counts as $status_slug => $count ) { 680 689 681 690 if ( 0 === $count ) { … … 683 692 } 684 693 685 if ( $status_ name === $request_status || ( empty( $request_status ) && 'all' === $status_name) ) {694 if ( $status_slug === $request_status || ( empty( $request_status ) && 'all' === $status_slug ) ) { 686 695 $status_list_item = '<li class="%1$s"><a href="%2$s" class="current">%3$s</a> (%4$d)</li>'; 687 696 } else { … … 689 698 } 690 699 691 $status_filter_url = ( 'all' === $status_name ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_name ); 700 $status_name = isset( $status_labels[ $status_slug ] ) ? $status_labels[ $status_slug ] : ucfirst( $status_slug ); 701 $status_filter_url = ( 'all' === $status_slug ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_slug ); 692 702 $status_filter_url = remove_query_arg( array( 'paged', 's' ), $status_filter_url ); 693 $status_list_items[] = sprintf( $status_list_item, esc_attr( $status_ name ), esc_url( $status_filter_url ), esc_html( ucfirst( $status_name )), absint( $count ) );703 $status_list_items[] = sprintf( $status_list_item, esc_attr( $status_slug ), esc_url( $status_filter_url ), esc_html( $status_name ), absint( $count ) ); 694 704 } 695 705 -
action-scheduler/trunk/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php
r2850016 r2910869 49 49 */ 50 50 public function process_action( $action_id, $context = '' ) { 51 // Temporarily override the error handler while we process the current action. 52 set_error_handler( 53 /** 54 * Temporary error handler which can catch errors and convert them into exceptions. This faciliates more 55 * robust error handling across all supported PHP versions. 56 * 57 * @throws Exception 58 * 59 * @param int $type Error level expressed as an integer. 60 * @param string $message Error message. 61 */ 62 function ( $type, $message ) { 63 throw new Exception( $message ); 64 }, 65 E_USER_ERROR | E_RECOVERABLE_ERROR 66 ); 67 68 /* 69 * The nested try/catch structure is required because we potentially need to convert thrown errors into 70 * exceptions (and an exception thrown from a catch block cannot be caught by a later catch block in the *same* 71 * structure). 72 */ 51 73 try { 52 $valid_action = false; 53 do_action( 'action_scheduler_before_execute', $action_id, $context ); 54 55 if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) { 56 do_action( 'action_scheduler_execution_ignored', $action_id, $context ); 57 return; 74 try { 75 $valid_action = false; 76 do_action( 'action_scheduler_before_execute', $action_id, $context ); 77 78 if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) { 79 do_action( 'action_scheduler_execution_ignored', $action_id, $context ); 80 return; 81 } 82 83 $valid_action = true; 84 do_action( 'action_scheduler_begin_execute', $action_id, $context ); 85 86 $action = $this->store->fetch_action( $action_id ); 87 $this->store->log_execution( $action_id ); 88 $action->execute(); 89 do_action( 'action_scheduler_after_execute', $action_id, $action, $context ); 90 $this->store->mark_complete( $action_id ); 91 } catch ( Throwable $e ) { 92 // Throwable is defined when executing under PHP 7.0 and up. We convert it to an exception, for 93 // compatibility with ActionScheduler_Logger. 94 throw new Exception( $e->getMessage(), $e->getCode(), $e->getPrevious() ); 58 95 } 59 60 $valid_action = true;61 do_action( 'action_scheduler_begin_execute', $action_id, $context );62 63 $action = $this->store->fetch_action( $action_id );64 $this->store->log_execution( $action_id );65 $action->execute();66 do_action( 'action_scheduler_after_execute', $action_id, $action, $context );67 $this->store->mark_complete( $action_id );68 96 } catch ( Exception $e ) { 69 if ( $valid_action ) { 70 $this->store->mark_failure( $action_id ); 71 do_action( 'action_scheduler_failed_execution', $action_id, $e, $context ); 72 } else { 73 do_action( 'action_scheduler_failed_validation', $action_id, $e, $context ); 74 } 97 // This catch block exists for compatibility with PHP 5.6. 98 $this->handle_action_error( $action_id, $e, $context, $valid_action ); 99 } finally { 100 restore_error_handler(); 75 101 } 76 102 77 103 if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) && $action->get_schedule()->is_recurring() ) { 78 104 $this->schedule_next_instance( $action, $action_id ); 105 } 106 } 107 108 /** 109 * Marks actions as either having failed execution or failed validation, as appropriate. 110 * 111 * @param int $action_id Action ID. 112 * @param Exception $e Exception instance. 113 * @param string $context Execution context. 114 * @param bool $valid_action If the action is valid. 115 * 116 * @return void 117 */ 118 private function handle_action_error( $action_id, $e, $context, $valid_action ) { 119 if ( $valid_action ) { 120 $this->store->mark_failure( $action_id ); 121 /** 122 * Runs when action execution fails. 123 * 124 * @param int $action_id Action ID. 125 * @param Exception $e Exception instance. 126 * @param string $context Execution context. 127 */ 128 do_action( 'action_scheduler_failed_execution', $action_id, $e, $context ); 129 } else { 130 /** 131 * Runs when action validation fails. 132 * 133 * @param int $action_id Action ID. 134 * @param Exception $e Exception instance. 135 * @param string $context Execution context. 136 */ 137 do_action( 'action_scheduler_failed_validation', $action_id, $e, $context ); 79 138 } 80 139 } … … 144 203 } 145 204 146 // Now let's fetch the first action (having the same hook) of *any status* ithin the same window.205 // Now let's fetch the first action (having the same hook) of *any status* within the same window. 147 206 unset( $query_args['status'] ); 148 207 $first_action_id_with_the_same_hook = $this->store->query_actions( $query_args ); 149 208 150 // If the IDs match, then actions for this hook must be consistently failing. 151 return $first_action_id_with_the_same_hook === $first_failing_action_id; 209 /** 210 * If a recurring action is assessed as consistently failing, it will not be rescheduled. This hook provides a 211 * way to observe and optionally override that assessment. 212 * 213 * @param bool $is_consistently_failing If the action is considered to be consistently failing. 214 * @param ActionScheduler_Action $action The action being assessed. 215 */ 216 return (bool) apply_filters( 217 'action_scheduler_recurring_action_is_consistently_failing', 218 $first_action_id_with_the_same_hook === $first_failing_action_id, 219 $action 220 ); 152 221 } 153 222 -
action-scheduler/trunk/classes/actions/ActionScheduler_Action.php
r2775803 r2910869 10 10 protected $schedule = NULL; 11 11 protected $group = ''; 12 13 /** 14 * Priorities are conceptually similar to those used for regular WordPress actions. 15 * Like those, a lower priority takes precedence over a higher priority and the default 16 * is 10. 17 * 18 * Unlike regular WordPress actions, the priority of a scheduled action is strictly an 19 * integer and should be kept within the bounds 0-255 (anything outside the bounds will 20 * be brought back into the acceptable range). 21 * 22 * @var int 23 */ 24 protected $priority = 10; 12 25 13 26 public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) { … … 94 107 return FALSE; 95 108 } 109 110 /** 111 * Sets the priority of the action. 112 * 113 * @param int $priority Priority level (lower is higher priority). Should be in the range 0-255. 114 * 115 * @return void 116 */ 117 public function set_priority( $priority ) { 118 if ( $priority < 0 ) { 119 $priority = 0; 120 } elseif ( $priority > 255 ) { 121 $priority = 255; 122 } 123 124 $this->priority = (int) $priority; 125 } 126 127 /** 128 * Gets the action priority. 129 * 130 * @return int 131 */ 132 public function get_priority() { 133 return $this->priority; 134 } 96 135 } -
action-scheduler/trunk/classes/data-stores/ActionScheduler_DBStore.php
r2850016 r2910869 26 26 protected static $max_index_length = 191; 27 27 28 /** @var array List of claim filters. */ 29 protected $claim_filters = [ 30 'group' => '', 31 'hooks' => '', 32 'exclude-groups' => '', 33 ]; 34 28 35 /** 29 36 * Initialize the data store … … 85 92 'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ), 86 93 'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize 87 'group_id' => $this->get_group_id( $action->get_group() ), 94 'group_id' => current( $this->get_group_ids( $action->get_group() ) ), 95 'priority' => $action->get_priority(), 88 96 ); 89 97 … … 173 181 ); 174 182 $pending_status_placeholders = implode( ', ', array_fill( 0, count( $pending_statuses ), '%s' ) ); 183 175 184 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pending_status_placeholders is hardcoded. 176 185 $where_clause = $wpdb->prepare( … … 243 252 * Get a group's ID based on its name/slug. 244 253 * 245 * @param string $slug The string name of a group. 246 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 247 * 248 * @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created. 249 */ 250 protected function get_group_id( $slug, $create_if_not_exists = true ) { 251 if ( empty( $slug ) ) { 252 return 0; 253 } 254 /** @var \wpdb $wpdb */ 255 global $wpdb; 256 $group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) ); 257 if ( empty( $group_id ) && $create_if_not_exists ) { 258 $group_id = $this->create_group( $slug ); 259 } 260 261 return $group_id; 254 * @param string|array $slugs The string name of a group, or names for several groups. 255 * @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group. 256 * 257 * @return array The group IDs, if they exist or were successfully created. May be empty. 258 */ 259 protected function get_group_ids( $slugs, $create_if_not_exists = true ) { 260 $slugs = (array) $slugs; 261 $group_ids = array(); 262 263 if ( empty( $slugs ) ) { 264 return array(); 265 } 266 267 /** @var \wpdb $wpdb */ 268 global $wpdb; 269 270 foreach ( $slugs as $slug ) { 271 $group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) ); 272 273 if ( empty( $group_id ) && $create_if_not_exists ) { 274 $group_id = $this->create_group( $slug ); 275 } 276 277 if ( $group_id ) { 278 $group_ids[] = $group_id; 279 } 280 } 281 282 return $group_ids; 262 283 } 263 284 … … 356 377 $group = $data->group ? $data->group : ''; 357 378 358 return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group );379 return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group, $data->priority ); 359 380 } 360 381 … … 798 819 799 820 /** 821 * Set a claim filter. 822 * 823 * @param string $filter_name Claim filter name. 824 * @param mixed $filter_values Values to filter. 825 * @return void 826 */ 827 public function set_claim_filter( $filter_name, $filter_values ) { 828 if ( isset( $this->claim_filters[ $filter_name ] ) ) { 829 $this->claim_filters[ $filter_name ] = $filter_values; 830 } 831 } 832 833 /** 834 * Get the claim filter value. 835 * 836 * @param string $filter_name Claim filter name. 837 * @return mixed 838 */ 839 public function get_claim_filter( $filter_name ) { 840 if ( isset( $this->claim_filters[ $filter_name ] ) ) { 841 return $this->claim_filters[ $filter_name ]; 842 } 843 844 return ''; 845 } 846 847 /** 800 848 * Mark actions claimed. 801 849 * … … 814 862 global $wpdb; 815 863 816 $now = as_get_datetime_object(); 817 $date = is_null( $before_date ) ? $now : clone $before_date; 818 864 $now = as_get_datetime_object(); 865 $date = is_null( $before_date ) ? $now : clone $before_date; 819 866 // can't use $wpdb->update() because of the <= condition. 820 867 $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s"; … … 825 872 ); 826 873 874 // Set claim filters. 875 if ( ! empty( $hooks ) ) { 876 $this->set_claim_filter( 'hooks', $hooks ); 877 } else { 878 $hooks = $this->get_claim_filter( 'hooks' ); 879 } 880 if ( ! empty( $group ) ) { 881 $this->set_claim_filter( 'group', $group ); 882 } else { 883 $group = $this->get_claim_filter( 'group' ); 884 } 885 827 886 $where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s'; 828 887 $params[] = $date->format( 'Y-m-d H:i:s' ); … … 835 894 } 836 895 896 $group_operator = 'IN'; 897 if ( empty( $group ) ) { 898 $group = $this->get_claim_filter( 'exclude-groups' ); 899 $group_operator = 'NOT IN'; 900 } 901 837 902 if ( ! empty( $group ) ) { 838 839 $group_id = $this->get_group_id( $group, false ); 840 841 // throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour. 842 if ( empty( $group_id ) ) { 843 /* translators: %s: group name */ 844 throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) ); 845 } 846 847 $where .= ' AND group_id = %d'; 848 $params[] = $group_id; 903 $group_ids = $this->get_group_ids( $group, false ); 904 905 // throw exception if no matching group(s) found, this matches ActionScheduler_wpPostStore's behaviour. 906 if ( empty( $group_ids ) ) { 907 throw new InvalidArgumentException( 908 sprintf( 909 /* translators: %s: group name(s) */ 910 _n( 911 'The group "%s" does not exist.', 912 'The groups "%s" do not exist.', 913 is_array( $group ) ? count( $group ) : 1, 914 'action-scheduler' 915 ), 916 $group 917 ) 918 ); 919 } 920 921 $id_list = implode( ',', array_map( 'intval', $group_ids ) ); 922 $where .= " AND group_id {$group_operator} ( $id_list )"; 849 923 } 850 924 … … 856 930 * @param string $order_by_sql 857 931 */ 858 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' );932 $order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC' ); 859 933 $params[] = $limit; 860 934 … … 913 987 914 988 $sql = $wpdb->prepare( 915 "SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ",989 "SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ORDER BY priority ASC", 916 990 $claim_id 917 991 ); … … 1006 1080 * Add execution message to action log. 1007 1081 * 1082 * @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress'). 1083 * 1008 1084 * @param int $action_id Action ID. 1009 1085 * … … 1016 1092 $sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d"; 1017 1093 $sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1018 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1094 1095 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 1096 $status_updated = $wpdb->query( $sql ); 1097 1098 if ( ! $status_updated ) { 1099 throw new Exception( 1100 sprintf( 1101 /* translators: 1: action ID. 2: status slug. */ 1102 __( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ), 1103 $action_id, 1104 self::STATUS_RUNNING 1105 ) 1106 ); 1107 } 1019 1108 } 1020 1109 -
action-scheduler/trunk/classes/data-stores/ActionScheduler_wpPostStore.php
r2775803 r2910869 937 937 * Log Execution. 938 938 * 939 * @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress'). 940 * 939 941 * @param string $action_id Action ID. 940 942 */ … … 948 950 949 951 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 950 $ wpdb->query(952 $status_updated = $wpdb->query( 951 953 $wpdb->prepare( 952 954 "UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s", … … 958 960 ) 959 961 ); 962 963 if ( ! $status_updated ) { 964 throw new Exception( 965 sprintf( 966 /* translators: 1: action ID. 2: status slug. */ 967 __( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ), 968 $action_id, 969 self::STATUS_RUNNING 970 ) 971 ); 972 } 960 973 } 961 974 -
action-scheduler/trunk/classes/migration/Runner.php
r2340383 r2910869 80 80 if ( $this->progress_bar ) { 81 81 /* translators: %d: amount of actions */ 82 $this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), number_format_i18n( $batch_size )) );82 $this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), $batch_size ) ); 83 83 $this->progress_bar->set_count( $batch_size ); 84 84 } -
action-scheduler/trunk/classes/schema/ActionScheduler_StoreSchema.php
r2850016 r2910869 17 17 * @var int Increment this value to trigger a schema update. 18 18 */ 19 protected $schema_version = 6;19 protected $schema_version = 7; 20 20 21 21 public function __construct() { … … 50 50 scheduled_date_gmt datetime NULL default '{$default_date}', 51 51 scheduled_date_local datetime NULL default '{$default_date}', 52 priority tinyint unsigned NOT NULL default '10', 52 53 args varchar($max_index_length), 53 54 schedule longtext, -
action-scheduler/trunk/functions.php
r2850016 r2910869 13 13 * @param string $group The group to assign this job to. 14 14 * @param bool $unique Whether the action should be unique. 15 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 15 16 * 16 17 * @return int The action ID. 17 18 */ 18 function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false ) {19 function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 19 20 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 20 21 return 0; … … 34 35 * @param array $args Action arguments. 35 36 * @param string $group Action group. 37 * @param int $priority Action priority. 36 38 */ 37 $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group );39 $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group, $priority ); 38 40 if ( null !== $pre ) { 39 41 return is_int( $pre ) ? $pre : 0; 40 42 } 41 43 42 return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique ); 44 return ActionScheduler::factory()->create( 45 array( 46 'type' => 'async', 47 'hook' => $hook, 48 'arguments' => $args, 49 'group' => $group, 50 'unique' => $unique, 51 'priority' => $priority, 52 ) 53 ); 43 54 } 44 55 … … 51 62 * @param string $group The group to assign this job to. 52 63 * @param bool $unique Whether the action should be unique. 64 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 53 65 * 54 66 * @return int The action ID. 55 67 */ 56 function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false ) {68 function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 57 69 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 58 70 return 0; … … 73 85 * @param array $args Action arguments. 74 86 * @param string $group Action group. 87 * @param int $priorities Action priority. 75 88 */ 76 $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group );89 $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority ); 77 90 if ( null !== $pre ) { 78 91 return is_int( $pre ) ? $pre : 0; 79 92 } 80 93 81 return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique ); 94 return ActionScheduler::factory()->create( 95 array( 96 'type' => 'single', 97 'hook' => $hook, 98 'arguments' => $args, 99 'when' => $timestamp, 100 'group' => $group, 101 'unique' => $unique, 102 'priority' => $priority, 103 ) 104 ); 82 105 } 83 106 … … 91 114 * @param string $group The group to assign this job to. 92 115 * @param bool $unique Whether the action should be unique. 116 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 93 117 * 94 118 * @return int The action ID. 95 119 */ 96 function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false ) { 97 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 120 function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 121 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 122 return 0; 123 } 124 125 $interval = (int) $interval_in_seconds; 126 127 // We expect an integer and allow it to be passed using float and string types, but otherwise 128 // should reject unexpected values. 129 if ( ! is_numeric( $interval_in_seconds ) || $interval_in_seconds != $interval ) { 130 _doing_it_wrong( 131 __METHOD__, 132 sprintf( 133 /* translators: 1: provided value 2: provided type. */ 134 esc_html__( 'An integer was expected but "%1$s" (%2$s) was received.', 'action-scheduler' ), 135 esc_html( $interval_in_seconds ), 136 esc_html( gettype( $interval_in_seconds ) ) 137 ), 138 '3.6.0' 139 ); 140 98 141 return 0; 99 142 } … … 114 157 * @param array $args Action arguments. 115 158 * @param string $group Action group. 159 * @param int $priority Action priority. 116 160 */ 117 $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group );161 $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority ); 118 162 if ( null !== $pre ) { 119 163 return is_int( $pre ) ? $pre : 0; 120 164 } 121 165 122 return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique ); 166 return ActionScheduler::factory()->create( 167 array( 168 'type' => 'recurring', 169 'hook' => $hook, 170 'arguments' => $args, 171 'when' => $timestamp, 172 'pattern' => $interval_in_seconds, 173 'group' => $group, 174 'unique' => $unique, 175 'priority' => $priority, 176 ) 177 ); 123 178 } 124 179 … … 144 199 * @param string $group The group to assign this job to. 145 200 * @param bool $unique Whether the action should be unique. 201 * @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255. 146 202 * 147 203 * @return int The action ID. 148 204 */ 149 function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false ) {205 function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { 150 206 if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { 151 207 return 0; … … 167 223 * @param array $args Action arguments. 168 224 * @param string $group Action group. 225 * @param int $priority Action priority. 169 226 */ 170 $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group );227 $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority ); 171 228 if ( null !== $pre ) { 172 229 return is_int( $pre ) ? $pre : 0; 173 230 } 174 231 175 return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique ); 232 return ActionScheduler::factory()->create( 233 array( 234 'type' => 'cron', 235 'hook' => $hook, 236 'arguments' => $args, 237 'when' => $timestamp, 238 'pattern' => $schedule, 239 'group' => $group, 240 'unique' => $unique, 241 'priority' => $priority, 242 ) 243 ); 176 244 } 177 245 -
action-scheduler/trunk/readme.txt
r2850016 r2910869 4 4 Requires at least: 5.2 5 5 Tested up to: 6.0 6 Stable tag: 3. 5.46 Stable tag: 3.6.0 7 7 License: GPLv3 8 8 Requires PHP: 5.6 … … 47 47 48 48 == Changelog == 49 50 = 3.6.0 - 2023-05-10 = 51 * Add $unique parameter to function signatures. 52 * Add a cast-to-int for extra safety before forming new DateTime object. 53 * Add a hook allowing exceptions for consistently failing recurring actions. 54 * Add action priorities. 55 * Add init hook. 56 * Always raise the time limit. 57 * Bump minimatch from 3.0.4 to 3.0.8. 58 * Bump yaml from 2.2.1 to 2.2.2. 59 * Defensive coding relating to gaps in declared schedule types. 60 * Do not process an action if it cannot be set to `in-progress`. 61 * Filter view labels (status names) should be translatable | #919. 62 * Fix WPCLI progress messages. 63 * Improve data-store initialization flow. 64 * Improve error handling across all supported PHP versions. 65 * Improve logic for flushing the runtime cache. 66 * Support exclusion of multiple groups. 67 * Update lint-staged and Node/NPM requirements. 68 * add CLI clean command. 69 * add CLI exclude-group filter. 70 * exclude past-due from list table all filter count. 71 * throwing an exception if as_schedule_recurring_action interval param is not of type integer. 49 72 50 73 = 3.5.4 - 2023-01-17 =
Note: See TracChangeset
for help on using the changeset viewer.