classes
2 years ago
deprecated
2 years ago
lib
2 years ago
action-scheduler.php
2 years ago
changelog.txt
2 years ago
functions.php
2 years ago
license.txt
2 years ago
readme.txt
2 years ago
functions.php
493 lines
| 1 | <?php |
| 2 | /** |
| 3 | * General API functions for scheduling actions |
| 4 | * |
| 5 | * @package ActionScheduler. |
| 6 | */ |
| 7 | |
| 8 | /** |
| 9 | * Enqueue an action to run one time, as soon as possible |
| 10 | * |
| 11 | * @param string $hook The hook to trigger. |
| 12 | * @param array $args Arguments to pass when the hook triggers. |
| 13 | * @param string $group The group to assign this job to. |
| 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. |
| 16 | * |
| 17 | * @return int The action ID. |
| 18 | */ |
| 19 | function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { |
| 20 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 21 | return 0; |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Provides an opportunity to short-circuit the default process for enqueuing async |
| 26 | * actions. |
| 27 | * |
| 28 | * Returning a value other than null from the filter will short-circuit the normal |
| 29 | * process. The expectation in such a scenario is that callbacks will return an integer |
| 30 | * representing the enqueued action ID (enqueued using some alternative process) or else |
| 31 | * zero. |
| 32 | * |
| 33 | * @param int|null $pre_option The value to return instead of the option value. |
| 34 | * @param string $hook Action hook. |
| 35 | * @param array $args Action arguments. |
| 36 | * @param string $group Action group. |
| 37 | * @param int $priority Action priority. |
| 38 | */ |
| 39 | $pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group, $priority ); |
| 40 | if ( null !== $pre ) { |
| 41 | return is_int( $pre ) ? $pre : 0; |
| 42 | } |
| 43 | |
| 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 | ); |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Schedule an action to run one time |
| 58 | * |
| 59 | * @param int $timestamp When the job will run. |
| 60 | * @param string $hook The hook to trigger. |
| 61 | * @param array $args Arguments to pass when the hook triggers. |
| 62 | * @param string $group The group to assign this job to. |
| 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. |
| 65 | * |
| 66 | * @return int The action ID. |
| 67 | */ |
| 68 | function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { |
| 69 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Provides an opportunity to short-circuit the default process for enqueuing single |
| 75 | * actions. |
| 76 | * |
| 77 | * Returning a value other than null from the filter will short-circuit the normal |
| 78 | * process. The expectation in such a scenario is that callbacks will return an integer |
| 79 | * representing the scheduled action ID (scheduled using some alternative process) or else |
| 80 | * zero. |
| 81 | * |
| 82 | * @param int|null $pre_option The value to return instead of the option value. |
| 83 | * @param int $timestamp When the action will run. |
| 84 | * @param string $hook Action hook. |
| 85 | * @param array $args Action arguments. |
| 86 | * @param string $group Action group. |
| 87 | * @param int $priorities Action priority. |
| 88 | */ |
| 89 | $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority ); |
| 90 | if ( null !== $pre ) { |
| 91 | return is_int( $pre ) ? $pre : 0; |
| 92 | } |
| 93 | |
| 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 | ); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Schedule a recurring action |
| 109 | * |
| 110 | * @param int $timestamp When the first instance of the job will run. |
| 111 | * @param int $interval_in_seconds How long to wait between runs. |
| 112 | * @param string $hook The hook to trigger. |
| 113 | * @param array $args Arguments to pass when the hook triggers. |
| 114 | * @param string $group The group to assign this job to. |
| 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. |
| 117 | * |
| 118 | * @return int The action ID. |
| 119 | */ |
| 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 | |
| 141 | return 0; |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Provides an opportunity to short-circuit the default process for enqueuing recurring |
| 146 | * actions. |
| 147 | * |
| 148 | * Returning a value other than null from the filter will short-circuit the normal |
| 149 | * process. The expectation in such a scenario is that callbacks will return an integer |
| 150 | * representing the scheduled action ID (scheduled using some alternative process) or else |
| 151 | * zero. |
| 152 | * |
| 153 | * @param int|null $pre_option The value to return instead of the option value. |
| 154 | * @param int $timestamp When the action will run. |
| 155 | * @param int $interval_in_seconds How long to wait between runs. |
| 156 | * @param string $hook Action hook. |
| 157 | * @param array $args Action arguments. |
| 158 | * @param string $group Action group. |
| 159 | * @param int $priority Action priority. |
| 160 | */ |
| 161 | $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority ); |
| 162 | if ( null !== $pre ) { |
| 163 | return is_int( $pre ) ? $pre : 0; |
| 164 | } |
| 165 | |
| 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 | ); |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * Schedule an action that recurs on a cron-like schedule. |
| 182 | * |
| 183 | * @param int $timestamp The first instance of the action will be scheduled |
| 184 | * to run at a time calculated after this timestamp matching the cron |
| 185 | * expression. This can be used to delay the first instance of the action. |
| 186 | * @param string $schedule A cron-link schedule string. |
| 187 | * @see http://en.wikipedia.org/wiki/Cron |
| 188 | * * * * * * * |
| 189 | * ┬ ┬ ┬ ┬ ┬ ┬ |
| 190 | * | | | | | | |
| 191 | * | | | | | + year [optional] |
| 192 | * | | | | +----- day of week (0 - 7) (Sunday=0 or 7) |
| 193 | * | | | +---------- month (1 - 12) |
| 194 | * | | +--------------- day of month (1 - 31) |
| 195 | * | +-------------------- hour (0 - 23) |
| 196 | * +------------------------- min (0 - 59) |
| 197 | * @param string $hook The hook to trigger. |
| 198 | * @param array $args Arguments to pass when the hook triggers. |
| 199 | * @param string $group The group to assign this job to. |
| 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. |
| 202 | * |
| 203 | * @return int The action ID. |
| 204 | */ |
| 205 | function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) { |
| 206 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Provides an opportunity to short-circuit the default process for enqueuing cron |
| 212 | * actions. |
| 213 | * |
| 214 | * Returning a value other than null from the filter will short-circuit the normal |
| 215 | * process. The expectation in such a scenario is that callbacks will return an integer |
| 216 | * representing the scheduled action ID (scheduled using some alternative process) or else |
| 217 | * zero. |
| 218 | * |
| 219 | * @param int|null $pre_option The value to return instead of the option value. |
| 220 | * @param int $timestamp When the action will run. |
| 221 | * @param string $schedule Cron-like schedule string. |
| 222 | * @param string $hook Action hook. |
| 223 | * @param array $args Action arguments. |
| 224 | * @param string $group Action group. |
| 225 | * @param int $priority Action priority. |
| 226 | */ |
| 227 | $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority ); |
| 228 | if ( null !== $pre ) { |
| 229 | return is_int( $pre ) ? $pre : 0; |
| 230 | } |
| 231 | |
| 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 | ); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Cancel the next occurrence of a scheduled action. |
| 248 | * |
| 249 | * While only the next instance of a recurring or cron action is unscheduled by this method, that will also prevent |
| 250 | * all future instances of that recurring or cron action from being run. Recurring and cron actions are scheduled in |
| 251 | * a sequence instead of all being scheduled at once. Each successive occurrence of a recurring action is scheduled |
| 252 | * only after the former action is run. If the next instance is never run, because it's unscheduled by this function, |
| 253 | * then the following instance will never be scheduled (or exist), which is effectively the same as being unscheduled |
| 254 | * by this method also. |
| 255 | * |
| 256 | * @param string $hook The hook that the job will trigger. |
| 257 | * @param array $args Args that would have been passed to the job. |
| 258 | * @param string $group The group the job is assigned to. |
| 259 | * |
| 260 | * @return int|null The scheduled action ID if a scheduled action was found, or null if no matching action found. |
| 261 | */ |
| 262 | function as_unschedule_action( $hook, $args = array(), $group = '' ) { |
| 263 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 264 | return 0; |
| 265 | } |
| 266 | $params = array( |
| 267 | 'hook' => $hook, |
| 268 | 'status' => ActionScheduler_Store::STATUS_PENDING, |
| 269 | 'orderby' => 'date', |
| 270 | 'order' => 'ASC', |
| 271 | 'group' => $group, |
| 272 | ); |
| 273 | if ( is_array( $args ) ) { |
| 274 | $params['args'] = $args; |
| 275 | } |
| 276 | |
| 277 | $action_id = ActionScheduler::store()->query_action( $params ); |
| 278 | |
| 279 | if ( $action_id ) { |
| 280 | try { |
| 281 | ActionScheduler::store()->cancel_action( $action_id ); |
| 282 | } catch ( Exception $exception ) { |
| 283 | ActionScheduler::logger()->log( |
| 284 | $action_id, |
| 285 | sprintf( |
| 286 | /* translators: %s is the name of the hook to be cancelled. */ |
| 287 | __( 'Caught exception while cancelling action: %s', 'action-scheduler' ), |
| 288 | esc_attr( $hook ) |
| 289 | ) |
| 290 | ); |
| 291 | |
| 292 | $action_id = null; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | return $action_id; |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Cancel all occurrences of a scheduled action. |
| 301 | * |
| 302 | * @param string $hook The hook that the job will trigger. |
| 303 | * @param array $args Args that would have been passed to the job. |
| 304 | * @param string $group The group the job is assigned to. |
| 305 | */ |
| 306 | function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) { |
| 307 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 308 | return; |
| 309 | } |
| 310 | if ( empty( $args ) ) { |
| 311 | if ( ! empty( $hook ) && empty( $group ) ) { |
| 312 | ActionScheduler_Store::instance()->cancel_actions_by_hook( $hook ); |
| 313 | return; |
| 314 | } |
| 315 | if ( ! empty( $group ) && empty( $hook ) ) { |
| 316 | ActionScheduler_Store::instance()->cancel_actions_by_group( $group ); |
| 317 | return; |
| 318 | } |
| 319 | } |
| 320 | do { |
| 321 | $unscheduled_action = as_unschedule_action( $hook, $args, $group ); |
| 322 | } while ( ! empty( $unscheduled_action ) ); |
| 323 | } |
| 324 | |
| 325 | /** |
| 326 | * Check if there is an existing action in the queue with a given hook, args and group combination. |
| 327 | * |
| 328 | * An action in the queue could be pending, in-progress or async. If the is pending for a time in |
| 329 | * future, its scheduled date will be returned as a timestamp. If it is currently being run, or an |
| 330 | * async action sitting in the queue waiting to be processed, in which case boolean true will be |
| 331 | * returned. Or there may be no async, in-progress or pending action for this hook, in which case, |
| 332 | * boolean false will be the return value. |
| 333 | * |
| 334 | * @param string $hook Name of the hook to search for. |
| 335 | * @param array $args Arguments of the action to be searched. |
| 336 | * @param string $group Group of the action to be searched. |
| 337 | * |
| 338 | * @return int|bool The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action. |
| 339 | */ |
| 340 | function as_next_scheduled_action( $hook, $args = null, $group = '' ) { |
| 341 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 342 | return false; |
| 343 | } |
| 344 | |
| 345 | $params = array( |
| 346 | 'hook' => $hook, |
| 347 | 'orderby' => 'date', |
| 348 | 'order' => 'ASC', |
| 349 | 'group' => $group, |
| 350 | ); |
| 351 | |
| 352 | if ( is_array( $args ) ) { |
| 353 | $params['args'] = $args; |
| 354 | } |
| 355 | |
| 356 | $params['status'] = ActionScheduler_Store::STATUS_RUNNING; |
| 357 | $action_id = ActionScheduler::store()->query_action( $params ); |
| 358 | if ( $action_id ) { |
| 359 | return true; |
| 360 | } |
| 361 | |
| 362 | $params['status'] = ActionScheduler_Store::STATUS_PENDING; |
| 363 | $action_id = ActionScheduler::store()->query_action( $params ); |
| 364 | if ( null === $action_id ) { |
| 365 | return false; |
| 366 | } |
| 367 | |
| 368 | $action = ActionScheduler::store()->fetch_action( $action_id ); |
| 369 | $scheduled_date = $action->get_schedule()->get_date(); |
| 370 | if ( $scheduled_date ) { |
| 371 | return (int) $scheduled_date->format( 'U' ); |
| 372 | } elseif ( null === $scheduled_date ) { // pending async action with NullSchedule. |
| 373 | return true; |
| 374 | } |
| 375 | |
| 376 | return false; |
| 377 | } |
| 378 | |
| 379 | /** |
| 380 | * Check if there is a scheduled action in the queue but more efficiently than as_next_scheduled_action(). |
| 381 | * |
| 382 | * It's recommended to use this function when you need to know whether a specific action is currently scheduled |
| 383 | * (pending or in-progress). |
| 384 | * |
| 385 | * @since 3.3.0 |
| 386 | * |
| 387 | * @param string $hook The hook of the action. |
| 388 | * @param array $args Args that have been passed to the action. Null will matches any args. |
| 389 | * @param string $group The group the job is assigned to. |
| 390 | * |
| 391 | * @return bool True if a matching action is pending or in-progress, false otherwise. |
| 392 | */ |
| 393 | function as_has_scheduled_action( $hook, $args = null, $group = '' ) { |
| 394 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 395 | return false; |
| 396 | } |
| 397 | |
| 398 | $query_args = array( |
| 399 | 'hook' => $hook, |
| 400 | 'status' => array( ActionScheduler_Store::STATUS_RUNNING, ActionScheduler_Store::STATUS_PENDING ), |
| 401 | 'group' => $group, |
| 402 | 'orderby' => 'none', |
| 403 | ); |
| 404 | |
| 405 | if ( null !== $args ) { |
| 406 | $query_args['args'] = $args; |
| 407 | } |
| 408 | |
| 409 | $action_id = ActionScheduler::store()->query_action( $query_args ); |
| 410 | |
| 411 | return null !== $action_id; |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * Find scheduled actions |
| 416 | * |
| 417 | * @param array $args Possible arguments, with their default values. |
| 418 | * 'hook' => '' - the name of the action that will be triggered. |
| 419 | * 'args' => NULL - the args array that will be passed with the action. |
| 420 | * 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. |
| 421 | * 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '='. |
| 422 | * 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone. |
| 423 | * 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '='. |
| 424 | * 'group' => '' - the group the action belongs to. |
| 425 | * 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING. |
| 426 | * 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID. |
| 427 | * 'per_page' => 5 - Number of results to return. |
| 428 | * 'offset' => 0. |
| 429 | * 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', 'date' or 'none'. |
| 430 | * 'order' => 'ASC'. |
| 431 | * |
| 432 | * @param string $return_format OBJECT, ARRAY_A, or ids. |
| 433 | * |
| 434 | * @return array |
| 435 | */ |
| 436 | function as_get_scheduled_actions( $args = array(), $return_format = OBJECT ) { |
| 437 | if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) { |
| 438 | return array(); |
| 439 | } |
| 440 | $store = ActionScheduler::store(); |
| 441 | foreach ( array( 'date', 'modified' ) as $key ) { |
| 442 | if ( isset( $args[ $key ] ) ) { |
| 443 | $args[ $key ] = as_get_datetime_object( $args[ $key ] ); |
| 444 | } |
| 445 | } |
| 446 | $ids = $store->query_actions( $args ); |
| 447 | |
| 448 | if ( 'ids' === $return_format || 'int' === $return_format ) { |
| 449 | return $ids; |
| 450 | } |
| 451 | |
| 452 | $actions = array(); |
| 453 | foreach ( $ids as $action_id ) { |
| 454 | $actions[ $action_id ] = $store->fetch_action( $action_id ); |
| 455 | } |
| 456 | |
| 457 | if ( ARRAY_A == $return_format ) { |
| 458 | foreach ( $actions as $action_id => $action_object ) { |
| 459 | $actions[ $action_id ] = get_object_vars( $action_object ); |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | return $actions; |
| 464 | } |
| 465 | |
| 466 | /** |
| 467 | * Helper function to create an instance of DateTime based on a given |
| 468 | * string and timezone. By default, will return the current date/time |
| 469 | * in the UTC timezone. |
| 470 | * |
| 471 | * Needed because new DateTime() called without an explicit timezone |
| 472 | * will create a date/time in PHP's timezone, but we need to have |
| 473 | * assurance that a date/time uses the right timezone (which we almost |
| 474 | * always want to be UTC), which means we need to always include the |
| 475 | * timezone when instantiating datetimes rather than leaving it up to |
| 476 | * the PHP default. |
| 477 | * |
| 478 | * @param mixed $date_string A date/time string. Valid formats are explained in http://php.net/manual/en/datetime.formats.php. |
| 479 | * @param string $timezone A timezone identifier, like UTC or Europe/Lisbon. The list of valid identifiers is available http://php.net/manual/en/timezones.php. |
| 480 | * |
| 481 | * @return ActionScheduler_DateTime |
| 482 | */ |
| 483 | function as_get_datetime_object( $date_string = null, $timezone = 'UTC' ) { |
| 484 | if ( is_object( $date_string ) && $date_string instanceof DateTime ) { |
| 485 | $date = new ActionScheduler_DateTime( $date_string->format( 'Y-m-d H:i:s' ), new DateTimeZone( $timezone ) ); |
| 486 | } elseif ( is_numeric( $date_string ) ) { |
| 487 | $date = new ActionScheduler_DateTime( '@' . $date_string, new DateTimeZone( $timezone ) ); |
| 488 | } else { |
| 489 | $date = new ActionScheduler_DateTime( null === $date_string ? 'now' : $date_string, new DateTimeZone( $timezone ) ); |
| 490 | } |
| 491 | return $date; |
| 492 | } |
| 493 |