if ( ! $wp_error && is_wp_error( $pre ) ) {
$crons = _get_cron_array();
$key = md5( serialize( $args ) );
unset( $crons[ $timestamp ][ $hook ][ $key ] );
if ( empty( $crons[ $timestamp ][ $hook ] ) ) {
unset( $crons[ $timestamp ][ $hook ] );
if ( empty( $crons[ $timestamp ] ) ) {
unset( $crons[ $timestamp ] );
return _set_cron_array( $crons, $wp_error );
* Unschedules all events attached to the hook with the specified arguments.
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean
* value which evaluates to FALSE. For information about casting to booleans see the
* {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
* the `===` operator for testing the return value of this function.
* @since 5.1.0 Return value modified to indicate success or failure,
* {@see 'pre_clear_scheduled_hook'} filter added to short-circuit the function.
* @since 5.7.0 The `$wp_error` parameter was added.
* @param string $hook Action hook, the execution of which will be unscheduled.
* @param array $args Optional. Array containing each separate argument to pass to the hook's callback function.
* Although not passed to a callback, these arguments are used to uniquely identify the
* event, so they should be the same as those used when originally scheduling the event.
* @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false.
* @return int|false|WP_Error On success an integer indicating number of events unscheduled (0 indicates no
* events were registered with the hook and arguments combination), false or WP_Error
* if unscheduling one or more events fail.
function wp_clear_scheduled_hook( $hook, $args = array(), $wp_error = false ) {
// Backward compatibility.
// Previously, this function took the arguments as discrete vars rather than an array like the rest of the API.
if ( ! is_array( $args ) ) {
_deprecated_argument( __FUNCTION__, '3.0.0', __( 'This argument has changed to an array to match the behavior of the other cron functions.' ) );
$args = array_slice( func_get_args(), 1 ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
* Filter to preflight or hijack clearing a scheduled hook.
* Returning a non-null value will short-circuit the normal unscheduling
* process, causing the function to return the filtered value instead.
* For plugins replacing wp-cron, return the number of events successfully
* unscheduled (zero if no events were registered with the hook) or false
* if unscheduling one or more events fails.
* @since 5.7.0 The `$wp_error` parameter was added, and a `WP_Error` object can now be returned.
* @param null|int|false|WP_Error $pre Value to return instead. Default null to continue unscheduling the event.
* @param string $hook Action hook, the execution of which will be unscheduled.
* @param array $args Arguments to pass to the hook's callback function.
* @param bool $wp_error Whether to return a WP_Error on failure.
$pre = apply_filters( 'pre_clear_scheduled_hook', null, $hook, $args, $wp_error );
if ( $wp_error && false === $pre ) {
'pre_clear_scheduled_hook_false',
__( 'A plugin prevented the hook from being cleared.' )
if ( ! $wp_error && is_wp_error( $pre ) ) {
* This logic duplicates wp_next_scheduled().
* It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing,
* and, wp_next_scheduled() returns the same schedule in an infinite loop.
$crons = _get_cron_array();
$key = md5( serialize( $args ) );
foreach ( $crons as $timestamp => $cron ) {
if ( isset( $cron[ $hook ][ $key ] ) ) {
$results[] = wp_unschedule_event( $timestamp, $hook, $args, true );
$errors = array_filter( $results, 'is_wp_error' );
array_walk( $errors, array( $error, 'merge_from' ) );
return count( $results );
* Unschedules all events attached to the hook.
* Can be useful for plugins when deactivating to clean up the cron queue.
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean
* value which evaluates to FALSE. For information about casting to booleans see the
* {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
* the `===` operator for testing the return value of this function.
* @since 5.1.0 Return value added to indicate success or failure.
* @since 5.7.0 The `$wp_error` parameter was added.
* @param string $hook Action hook, the execution of which will be unscheduled.
* @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false.
* @return int|false|WP_Error On success an integer indicating number of events unscheduled (0 indicates no
* events were registered on the hook), false or WP_Error if unscheduling fails.
function wp_unschedule_hook( $hook, $wp_error = false ) {
* Filter to preflight or hijack clearing all events attached to the hook.
* Returning a non-null value will short-circuit the normal unscheduling
* process, causing the function to return the filtered value instead.
* For plugins replacing wp-cron, return the number of events successfully
* unscheduled (zero if no events were registered with the hook) or false
* if unscheduling one or more events fails.
* @since 5.7.0 The `$wp_error` parameter was added, and a `WP_Error` object can now be returned.
* @param null|int|false|WP_Error $pre Value to return instead. Default null to continue unscheduling the hook.
* @param string $hook Action hook, the execution of which will be unscheduled.
* @param bool $wp_error Whether to return a WP_Error on failure.
$pre = apply_filters( 'pre_unschedule_hook', null, $hook, $wp_error );
if ( $wp_error && false === $pre ) {
'pre_unschedule_hook_false',
__( 'A plugin prevented the hook from being cleared.' )
if ( ! $wp_error && is_wp_error( $pre ) ) {
$crons = _get_cron_array();
foreach ( $crons as $timestamp => $args ) {
if ( ! empty( $crons[ $timestamp ][ $hook ] ) ) {
$results[] = count( $crons[ $timestamp ][ $hook ] );
unset( $crons[ $timestamp ][ $hook ] );
if ( empty( $crons[ $timestamp ] ) ) {
unset( $crons[ $timestamp ] );
* If the results are empty (zero events to unschedule), no attempt
* to update the cron array is required.
if ( empty( $results ) ) {
$set = _set_cron_array( $crons, $wp_error );
return array_sum( $results );
* Retrieve a scheduled event.
* Retrieve the full event object for a given event, if no timestamp is specified the next
* scheduled event is returned.
* @param string $hook Action hook of the event.
* @param array $args Optional. Array containing each separate argument to pass to the hook's callback function.
* Although not passed to a callback, these arguments are used to uniquely identify the
* event, so they should be the same as those used when originally scheduling the event.
* @param int|null $timestamp Optional. Unix timestamp (UTC) of the event. If not specified, the next scheduled event
* is returned. Default null.
* @return object|false The event object. False if the event does not exist.
function wp_get_scheduled_event( $hook, $args = array(), $timestamp = null ) {
* Filter to preflight or hijack retrieving a scheduled event.
* Returning a non-null value will short-circuit the normal process,
* returning the filtered value instead.
* Return false if the event does not exist, otherwise an event object
* @param null|false|object $pre Value to return instead. Default null to continue retrieving the event.
* @param string $hook Action hook of the event.
* @param array $args Array containing each separate argument to pass to the hook's callback function.
* Although not passed to a callback, these arguments are used to uniquely identify
* @param int|null $timestamp Unix timestamp (UTC) of the event. Null to retrieve next scheduled event.
$pre = apply_filters( 'pre_get_scheduled_event', null, $hook, $args, $timestamp );
if ( null !== $timestamp && ! is_numeric( $timestamp ) ) {
$crons = _get_cron_array();
$key = md5( serialize( $args ) );
foreach ( $crons as $timestamp => $cron ) {
if ( isset( $cron[ $hook ][ $key ] ) ) {
} elseif ( ! isset( $crons[ $timestamp ][ $hook ][ $key ] ) ) {
'timestamp' => $timestamp,
'schedule' => $crons[ $timestamp ][ $hook ][ $key ]['schedule'],
if ( isset( $crons[ $timestamp ][ $hook ][ $key ]['interval'] ) ) {
$event->interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
* Retrieve the next timestamp for an event.
* @param string $hook Action hook of the event.
* @param array $args Optional. Array containing each separate argument to pass to the hook's callback function.
* Although not passed to a callback, these arguments are used to uniquely identify the
* event, so they should be the same as those used when originally scheduling the event.
* @return int|false The Unix timestamp of the next time the event will occur. False if the event doesn't exist.
function wp_next_scheduled( $hook, $args = array() ) {
$next_event = wp_get_scheduled_event( $hook, $args );
return $next_event->timestamp;
* Sends a request to run cron through HTTP request that doesn't halt page loading.
* @since 5.1.0 Return values added.
* @param int $gmt_time Optional. Unix timestamp (UTC). Default 0 (current time is used).
* @return bool True if spawned, false if no events spawned.
function spawn_cron( $gmt_time = 0 ) {
$gmt_time = microtime( true );
if ( defined( 'DOING_CRON' ) || isset( $_GET['doing_wp_cron'] ) ) {
* Get the cron lock, which is a Unix timestamp of when the last cron was spawned
* and has not finished running.
* Multiple processes on multiple web servers can run this code concurrently,
* this lock attempts to make spawning as atomic as possible.
$lock = get_transient( 'doing_cron' );
if ( $lock > $gmt_time + 10 * MINUTE_IN_SECONDS ) {
// Don't run if another process is currently running it or more than once every 60 sec.
if ( $lock + WP_CRON_LOCK_TIMEOUT > $gmt_time ) {
$crons = wp_get_ready_cron_jobs();
$keys = array_keys( $crons );
if ( isset( $keys[0] ) && $keys[0] > $gmt_time ) {
if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
if ( 'GET' !== $_SERVER['REQUEST_METHOD'] || defined( 'DOING_AJAX' ) || defined( 'XMLRPC_REQUEST' ) ) {
$doing_wp_cron = sprintf( '%.22F', $gmt_time );
set_transient( 'doing_cron', $doing_wp_cron );
wp_redirect( add_query_arg( 'doing_wp_cron', $doing_wp_cron, wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
// Flush any buffers and send the headers.
include_once ABSPATH . 'wp-cron.php';
// Set the cron lock with the current unix timestamp, when the cron is being spawned.
$doing_wp_cron = sprintf( '%.22F', $gmt_time );
set_transient( 'doing_cron', $doing_wp_cron );
* Filters the cron request arguments.
* @since 4.5.0 The `$doing_wp_cron` parameter was added.
* @param array $cron_request_array {
* An array of cron request URL arguments.
* @type string $url The cron request URL.
* @type int $key The 22 digit GMT microtime.
* An array of cron request arguments.
* @type int $timeout The request timeout in seconds. Default .01 seconds.
* @type bool $blocking Whether to set blocking for the request. Default false.
* @type bool $sslverify Whether SSL should be verified for the request. Default false.
* @param string $doing_wp_cron The unix timestamp of the cron lock.
$cron_request = apply_filters(
'url' => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ),
/** This filter is documented in wp-includes/class-wp-http-streams.php */
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
$result = wp_remote_post( $cron_request['url'], $cron_request['args'] );
return ! is_wp_error( $result );
* Register _wp_cron() to run on the {@see 'wp_loaded'} action.
* If the {@see 'wp_loaded'} action has already fired, this function calls
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean
* value which evaluates to FALSE. For information about casting to booleans see the
* {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
* the `===` operator for testing the return value of this function.
* @since 5.1.0 Return value added to indicate success or failure.
* @since 5.7.0 Functionality moved to _wp_cron() to which this becomes a wrapper.
* @return bool|int|void On success an integer indicating number of events spawned (0 indicates no
* events needed to be spawned), false if spawning fails for one or more events or
* void if the function registered _wp_cron() to run on the action.
if ( did_action( 'wp_loaded' ) ) {
add_action( 'wp_loaded', '_wp_cron', 20 );
* Run scheduled callbacks or spawn cron for all scheduled events.
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean
* value which evaluates to FALSE. For information about casting to booleans see the
* {@link https://www.php.net/manual/en/language.types.boolean.php PHP documentation}. Use
* the `===` operator for testing the return value of this function.
* @return int|false On success an integer indicating number of events spawned (0 indicates no
* events needed to be spawned), false if spawning fails for one or more events.
// Prevent infinite loops caused by lack of wp-cron.php.
if ( strpos( $_SERVER['REQUEST_URI'], '/wp-cron.php' ) !== false || ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ) {
$crons = wp_get_ready_cron_jobs();
$gmt_time = microtime( true );
$keys = array_keys( $crons );
if ( isset( $keys[0] ) && $keys[0] > $gmt_time ) {
$schedules = wp_get_schedules();
foreach ( $crons as $timestamp => $cronhooks ) {
if ( $timestamp > $gmt_time ) {
foreach ( (array) $cronhooks as $hook => $args ) {
if ( isset( $schedules[ $hook ]['callback'] ) && ! call_user_func( $schedules[ $hook ]['callback'] ) ) {
$results[] = spawn_cron( $gmt_time );