Make WordPress Core


Ignore:
Timestamp:
07/27/2018 02:22:50 AM (7 years ago)
Author:
peterwilsoncc
Message:

Cron: Add hooks and a function to allow hijacking cron implementation.

This allows sites with a large cron option or a custom cron implementation to hijack the cron option to store cron data using custom functionality.

wp_get_scheduled_event() is new function to retrieve the event object for a given event based on the hook name, arguments and timestamp. If no timestamp is specified the next occurence is returned.

Preflight filters are added to all functions that read from or modify the cron option: pre_schedule_event, pre_reschedule_event, pre_unschedule_event, pre_clear_scheduled_hook, pre_unschedule_hook, pre_get_scheduled_event and pre_next_scheduled.

Additionally, the post scheduling hooks next_scheduled and get_schedule to allow plugins to modify an event after retrieving it from WordPress.

Props rmccue, DavidAnderson, ethitter, peterwilsoncc.
Fixes #32656.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/cron.php

    r43050 r43540  
    2222 *
    2323 * @since 2.1.0
    24  * @since 5.0.0 Return value modified to boolean indicating success or failure.
     24 * @since 5.0.0 Return value modified to boolean indicating success or failure,
     25 *              {@see pre_schedule_event} filter added to short-circuit the function.
    2526 *
    2627 * @link https://codex.wordpress.org/Function_Reference/wp_schedule_single_event
     
    3738    }
    3839
    39     // Don't schedule a duplicate if there's already an identical event due within 10 minutes of it
    40     $next = wp_next_scheduled( $hook, $args );
    41     if ( $next && abs( $next - $timestamp ) <= 10 * MINUTE_IN_SECONDS ) {
    42         return false;
    43     }
    44 
    45     $crons = _get_cron_array();
    4640    $event = (object) array(
    4741        'hook'      => $hook,
     
    5246
    5347    /**
    54      * Filters a single event before it is scheduled.
    55      *
    56      * @since 3.1.0
    57      *
    58      * @param stdClass $event {
     48     * Filter to preflight or hijack scheduling an event.
     49     *
     50     * Returning a non-null value will short-circuit adding the event to the
     51     * cron array, causing the function to return the filtered value instead.
     52     *
     53     * Both single events and recurring events are passed through this filter;
     54     * single events have `$event->schedule` as false, whereas recurring events
     55     * have this set to a recurrence from {@see wp_get_schedules}. Recurring
     56     * events also have the integer recurrence interval set as `$event->interval`.
     57     *
     58     * For plugins replacing wp-cron, it is recommended you check for an
     59     * identical event within ten minutes and apply the {@see schedule_event}
     60     * filter to check if another plugin has disallowed the event before scheduling.
     61     *
     62     * Return true if the event was scheduled, false if not.
     63     *
     64     * @since 5.0.0
     65     *
     66     * @param null|bool $pre   Value to return instead. Default null to continue adding the event.
     67     * @param stdClass  $event {
    5968     *     An object containing an event's data.
    6069     *
     
    6675     * }
    6776     */
     77    $pre = apply_filters( 'pre_schedule_event', null, $event );
     78    if ( null !== $pre ) {
     79        return $pre;
     80    }
     81
     82    // Don't schedule a duplicate if there's already an identical event due within 10 minutes of it
     83    $next = wp_next_scheduled( $hook, $args );
     84    if ( $next && abs( $next - $timestamp ) <= 10 * MINUTE_IN_SECONDS ) {
     85        return false;
     86    }
     87
     88    /**
     89     * Filters a single event before it is scheduled.
     90     *
     91     * @since 3.1.0
     92     *
     93     * @param stdClass $event {
     94     *     An object containing an event's data.
     95     *
     96     *     @type string       $hook      Action hook to execute when the event is run.
     97     *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
     98     *     @type string|false $schedule  How often the event should subsequently recur.
     99     *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
     100     *     @type int          $interval  The interval time in seconds for the schedule. Only present for recurring events.
     101     * }
     102     */
    68103    $event = apply_filters( 'schedule_event', $event );
    69104
     
    75110    $key = md5( serialize( $event->args ) );
    76111
     112    $crons = _get_cron_array();
    77113    $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
    78114        'schedule' => $event->schedule,
     
    102138 *
    103139 * @since 2.1.0
    104  * @since 5.0.0 Return value modified to boolean indicating success or failure.
     140 * @since 5.0.0 Return value modified to boolean indicating success or failure,
     141 *              {@see pre_schedule_event} filter added to short-circuit the function.
    105142 *
    106143 * @link https://codex.wordpress.org/Function_Reference/wp_schedule_event
     
    118155    }
    119156
    120     $crons     = _get_cron_array();
    121157    $schedules = wp_get_schedules();
    122158
     
    132168        'interval'  => $schedules[ $recurrence ]['interval'],
    133169    );
     170
     171    /** This filter is documented in wp-includes/cron.php */
     172    $pre = apply_filters( 'pre_schedule_event', null, $event );
     173    if ( null !== $pre ) {
     174        return $pre;
     175    }
     176
    134177    /** This filter is documented in wp-includes/cron.php */
    135178    $event = apply_filters( 'schedule_event', $event );
     
    142185    $key = md5( serialize( $event->args ) );
    143186
     187    $crons = _get_cron_array();
    144188    $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
    145189        'schedule' => $event->schedule,
     
    155199 *
    156200 * @since 2.1.0
    157  * @since 5.0.0 Return value modified to boolean indicating success or failure.
     201 * @since 5.0.0 Return value modified to boolean indicating success or failure,
     202 *              {@see pre_reschedule_event} filter added to short-circuit the function.
    158203 *
    159204 * @param int    $timestamp  Unix timestamp (UTC) for when to next run the event.
     
    169214    }
    170215
    171     $crons     = _get_cron_array();
    172216    $schedules = wp_get_schedules();
    173     $key       = md5( serialize( $args ) );
    174217    $interval  = 0;
    175218
    176     // First we try to get it from the schedule
     219    // First we try to get the interval from the schedule.
    177220    if ( isset( $schedules[ $recurrence ] ) ) {
    178221        $interval = $schedules[ $recurrence ]['interval'];
    179222    }
    180     // Now we try to get it from the saved interval in case the schedule disappears
    181     if ( 0 == $interval ) {
    182         $interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
    183     }
     223
     224    // Now we try to get it from the saved interval in case the schedule disappears.
     225    if ( 0 === $interval ) {
     226        $scheduled_event = wp_get_scheduled_event( $hook, $args, $timestamp );
     227        if ( $scheduled_event && isset( $scheduled_event->interval ) ) {
     228            $interval = $scheduled_event->interval;
     229        }
     230    }
     231
     232    $event = (object) array(
     233        'hook'      => $hook,
     234        'timestamp' => $timestamp,
     235        'schedule'  => $recurrence,
     236        'args'      => $args,
     237        'interval'  => $interval,
     238    );
     239
     240    /**
     241     * Filter to preflight or hijack rescheduling of events.
     242     *
     243     * Returning a non-null value will short-circuit the normal rescheduling
     244     * process, causing the function to return the filtered value instead.
     245     *
     246     * For plugins replacing wp-cron, return true if the event was successfully
     247     * rescheduled, false if not.
     248     *
     249     * @since 5.0.0
     250     *
     251     * @param null|bool $pre   Value to return instead. Default null to continue adding the event.
     252     * @param stdClass  $event {
     253     *     An object containing an event's data.
     254     *
     255     *     @type string       $hook      Action hook to execute when the event is run.
     256     *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
     257     *     @type string|false $schedule  How often the event should subsequently recur.
     258     *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
     259     *     @type int          $interval  The interval time in seconds for the schedule. Only present for recurring events.
     260     * }
     261     */
     262    $pre = apply_filters( 'pre_reschedule_event', null, $event );
     263    if ( null !== $pre ) {
     264        return $pre;
     265    }
     266
    184267    // Now we assume something is wrong and fail to schedule
    185268    if ( 0 == $interval ) {
     
    205288 *
    206289 * @since 2.1.0
    207  * @since 5.0.0 Return value modified to boolean indicating success or failure.
     290 * @since 5.0.0 Return value modified to boolean indicating success or failure,
     291 *              {@see pre_unschedule_event} filter added to short-circuit the function.
    208292 *
    209293 * @param int    $timestamp Unix timestamp (UTC) of the event.
     
    220304    }
    221305
     306    /**
     307     * Filter to preflight or hijack unscheduling of events.
     308     *
     309     * Returning a non-null value will short-circuit the normal unscheduling
     310     * process, causing the function to return the filtered value instead.
     311     *
     312     * For plugins replacing wp-cron, return true if the event was successfully
     313     * unscheduled, false if not.
     314     *
     315     * @since 5.0.0
     316     *
     317     * @param null|bool $pre       Value to return instead. Default null to continue unscheduling the event.
     318     * @param int       $timestamp Timestamp for when to run the event.
     319     * @param string    $hook      Action hook, the execution of which will be unscheduled.
     320     * @param array     $args      Arguments to pass to the hook's callback function.
     321     */
     322    $pre = apply_filters( 'pre_unschedule_event', null, $timestamp, $hook, $args );
     323    if ( null !== $pre ) {
     324        return $pre;
     325    }
     326
    222327    $crons = _get_cron_array();
    223328    $key   = md5( serialize( $args ) );
     
    241346 *
    242347 * @since 2.1.0
    243  * @since 5.0.0 Return value modified to indicate success or failure.
     348 * @since 5.0.0 Return value modified to indicate success or failure,
     349 *              {@see pre_clear_scheduled_hook} filter added to short-circuit the function.
    244350 *
    245351 * @param string $hook Action hook, the execution of which will be unscheduled.
     
    257363    }
    258364
     365    /**
     366     * Filter to preflight or hijack clearing a scheduled hook.
     367     *
     368     * Returning a non-null value will short-circuit the normal unscheduling
     369     * process, causing the function to return the filtered value instead.
     370     *
     371     * For plugins replacing wp-cron, return the number of events successfully
     372     * unscheduled (zero if no events were registered with the hook) or false
     373     * if unscheduling one or more events fails.
     374     *
     375     * @since 5.0.0
     376     *
     377     * @param null|array $pre  Value to return instead. Default null to continue unscheduling the event.
     378     * @param string     $hook Action hook, the execution of which will be unscheduled.
     379     * @param array      $args Arguments to pass to the hook's callback function.
     380     */
     381    $pre = apply_filters( 'pre_clear_scheduled_hook', null, $hook, $args );
     382    if ( null !== $pre ) {
     383        return $pre;
     384    }
     385
    259386    // This logic duplicates wp_next_scheduled()
    260387    // It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing,
     
    296423 */
    297424function wp_unschedule_hook( $hook ) {
     425    /**
     426     * Filter to preflight or hijack clearing all events attached to the hook.
     427     *
     428     * Returning a non-null value will short-circuit the normal unscheduling
     429     * process, causing the function to return the filtered value instead.
     430     *
     431     * For plugins replacing wp-cron, return the number of events successfully
     432     * unscheduled (zero if no events were registered with the hook) or false
     433     * if unscheduling one or more events fails.
     434     *
     435     * @since 5.0.0
     436     *
     437     * @param null|array $pre  Value to return instead. Default null to continue unscheduling the hook.
     438     * @param string     $hook Action hook, the execution of which will be unscheduled.
     439     */
     440    $pre = apply_filters( 'pre_unschedule_hook', null, $hook );
     441    if ( null !== $pre ) {
     442        return $pre;
     443    }
     444
    298445    $crons = _get_cron_array();
    299446    if ( empty( $crons ) ) {
     
    327474
    328475/**
     476 * Retrieve a scheduled event.
     477 *
     478 * Retrieve the full event object for a given event.
     479 *
     480 * @since 5.0.0
     481 *
     482 * @param string   $hook      Action hook of the event.
     483 * @param array    $args      Optional. Array containing each separate argument to pass to the hook's callback function.
     484 *                            Although not passed to a callback, these arguments are used to uniquely identify the
     485 *                            event, so they should be the same as those used when originally scheduling the event.
     486 * @param int|null $timestamp Optional. Unix timestamp (UTC) of the event. If not specified, the next scheduled event is returned.
     487 * @return bool|object The event object. False if the event does not exist.
     488 */
     489function wp_get_scheduled_event( $hook, $args = array(), $timestamp = null ) {
     490    if ( ! $timestamp ) {
     491        // Get the next scheduled event.
     492        $timestamp = wp_next_scheduled( $hook, $args );
     493    }
     494
     495    /**
     496     * Filter to preflight or hijack retrieving a scheduled event.
     497     *
     498     * Returning a non-null value will short-circuit the normal process,
     499     * returning the filtered value instead.
     500     *
     501     * Return false if the event does not exist, otherwise an event object
     502     * should be returned.
     503     *
     504     * @since 5.0.0
     505     *
     506     * @param null|bool $pre       Value to return instead. Default null to continue retrieving the event.
     507     * @param string    $hook      Action hook of the event.
     508     * @param array     $args      Array containing each separate argument to pass to the hook's callback function.
     509     *                             Although not passed to a callback, these arguments are used to uniquely identify the
     510     *                             event.
     511     * @param int       $timestamp Unix timestamp (UTC) of the event.
     512     */
     513    $pre = apply_filters( 'pre_get_scheduled_event', null, $hook, $args, $timestamp );
     514    if ( null !== $pre ) {
     515        return $pre;
     516    }
     517
     518    $crons = _get_cron_array();
     519    $key   = md5( serialize( $args ) );
     520
     521    if ( ! $timestamp || ! isset( $crons[ $timestamp ] ) ) {
     522        // No such event.
     523        return false;
     524    }
     525
     526    if ( ! isset( $crons[ $timestamp ][ $hook ] ) || ! isset( $crons[ $timestamp ][ $hook ][ $key ] ) ) {
     527        return false;
     528    }
     529
     530    $event = (object) array(
     531        'hook'      => $hook,
     532        'timestamp' => $timestamp,
     533        'schedule'  => $crons[ $timestamp ][ $hook ][ $key ]['schedule'],
     534        'args'      => $args,
     535    );
     536
     537    if ( isset( $crons[ $timestamp ][ $hook ][ $key ]['interval'] ) ) {
     538        $event->interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
     539    }
     540
     541    return $event;
     542}
     543
     544/**
    329545 * Retrieve the next timestamp for an event.
    330546 *
    331547 * @since 2.1.0
     548 * @since 5.0.0 {@see pre_next_scheduled} and {@see next_scheduled} filters added.
    332549 *
    333550 * @param string $hook Action hook of the event.
     
    338555 */
    339556function wp_next_scheduled( $hook, $args = array() ) {
     557    /**
     558     * Filter to preflight or hijack retrieving the next scheduled event timestamp.
     559     *
     560     * Returning a non-null value will short-circuit the normal retrieval
     561     * process, causing the function to return the filtered value instead.
     562     *
     563     * Pass the timestamp of the next event if it exists, false if not.
     564     *
     565     * @since 5.0.0
     566     *
     567     * @param null|bool $pre       Value to return instead. Default null to continue unscheduling the event.
     568     * @param string    $hook      Action hook of the event.
     569     * @param array     $args      Arguments to pass to the hook's callback function.
     570     */
     571    $pre = apply_filters( 'pre_next_scheduled', null, $hook, $args );
     572    if ( null !== $pre ) {
     573        return $pre;
     574    }
     575
    340576    $crons = _get_cron_array();
    341577    $key   = md5( serialize( $args ) );
    342     if ( empty( $crons ) ) {
    343         return false;
    344     }
    345     foreach ( $crons as $timestamp => $cron ) {
    346         if ( isset( $cron[ $hook ][ $key ] ) ) {
    347             return $timestamp;
     578    $next  = false;
     579
     580    if ( ! empty( $crons ) ) {
     581        foreach ( $crons as $timestamp => $cron ) {
     582            if ( isset( $cron[ $hook ][ $key ] ) ) {
     583                $next = $timestamp;
     584                break;
     585            }
    348586        }
    349587    }
    350     return false;
     588
     589    /**
     590     * Filter the next scheduled event timestamp.
     591     *
     592     * @since 5.0.0
     593     *
     594     * @param int|bool $next The UNIX timestamp when the scheduled event will next occur, or false if not found.
     595     * @param string   $hook Action hook to execute when cron is run.
     596     * @param array    $args Arguments to be passed to the callback function. Used for deduplicating events.
     597     */
     598    return apply_filters( 'next_scheduled', $next, $hook, $args );
    351599}
    352600
     
    573821 *
    574822 * @since 2.1.0
     823 * @since 5.0.0 {@see get_schedule} filter added.
    575824 *
    576825 * @param string $hook Action hook to identify the event.
     
    579828 */
    580829function wp_get_schedule( $hook, $args = array() ) {
    581     $crons = _get_cron_array();
    582     $key   = md5( serialize( $args ) );
    583     if ( empty( $crons ) ) {
    584         return false;
    585     }
    586     foreach ( $crons as $timestamp => $cron ) {
    587         if ( isset( $cron[ $hook ][ $key ] ) ) {
    588             return $cron[ $hook ][ $key ]['schedule'];
    589         }
    590     }
    591     return false;
     830    $schedule = false;
     831    $event    = wp_get_scheduled_event( $hook, $args );
     832
     833    if ( $event ) {
     834        $schedule = $event->schedule;
     835    }
     836
     837    /**
     838     * Filter the schedule for a hook.
     839     *
     840     * @since 5.0.0
     841     *
     842     * @param string|bool $schedule Schedule for the hook. False if not found.
     843     * @param string      $hook     Action hook to execute when cron is run.
     844     * @param array       $args     Optional. Arguments to pass to the hook's callback function.
     845     */
     846    return apply_filters( 'get_schedule', $schedule, $hook, $args );
    592847}
    593848
Note: See TracChangeset for help on using the changeset viewer.