diff --git a/src/event.php b/src/event.php index 7b4f90e..b70dee0 100644 --- a/src/event.php +++ b/src/event.php @@ -31,10 +31,26 @@ function run( $hookname, $sig ) { $event = Event::create_immediate( $hookname, $data['args'] ); delete_transient( 'doing_cron' ); - $scheduled = force_schedule_single_event( $hookname, $event->args ); // UTC + $scheduled = wp_schedule_single_event( 1, $hookname, $event->args, true ); // UTC if ( is_wp_error( $scheduled ) ) { - return $scheduled; + if ( 'duplicate_event' !== $scheduled->get_error_code() ) { + return $scheduled; + } + + // A duplicate_event error can be a false positive: Cavalcade's pre_schedule_event + // uses a 10-minute window centred on timestamp=1, so it flags the original near-future + // event as a duplicate without actually scheduling anything at timestamp=1. + // If no job exists at timestamp=1, unschedule the future one and retry. + $next = wp_next_scheduled( $hookname, $event->args ); + if ( false !== $next && 1 !== $next ) { + wp_unschedule_event( $next, $hookname, $event->args ); + $scheduled = wp_schedule_single_event( 1, $hookname, $event->args, true ); + if ( is_wp_error( $scheduled ) ) { + return $scheduled; + } + } + // else: a job at timestamp=1 already exists, already queued to run immediately. } add_filter( 'cron_request', function ( array $cron_request_array ) { @@ -67,48 +83,6 @@ function run( $hookname, $sig ) { ); } -/** - * Forcibly schedules a single event for the purpose of manually running it. - * - * This is used instead of `wp_schedule_single_event()` to avoid the duplicate check that's otherwise performed. - * - * @param string $hook Action hook to execute when the event is run. - * @param mixed[] $args Optional. Array containing each separate argument to pass to the hook's callback function. - * @return true|WP_Error True if event successfully scheduled. WP_Error on failure. - */ -function force_schedule_single_event( $hook, $args = array() ) { - $event = (object) array( - 'hook' => $hook, - 'timestamp' => 1, - 'schedule' => false, - 'args' => $args, - ); - $crons = get_core_cron_array(); - $key = md5( serialize( $event->args ) ); - - $crons[ $event->timestamp ][ $event->hook ][ $key ] = array( - 'schedule' => $event->schedule, - 'args' => $event->args, - ); - ksort( $crons ); - - $result = _set_cron_array( $crons ); - - // Not using the WP_Error from `_set_cron_array()` here so we can provide a more specific error message. - if ( false === $result ) { - return new WP_Error( - 'could_not_add', - sprintf( - /* translators: %s: The name of the cron event. */ - __( 'Failed to schedule the cron event %s.', 'wp-crontrol' ), - $hook - ) - ); - } - - return true; -} - /** * Adds a new cron event. *