WordPress.org

Make WordPress Core

Ticket #32656: 32656.6.diff

File 32656.6.diff, 23.7 KB (added by peterwilsoncc, 9 months ago)
  • src/wp-includes/cron.php

    diff --git src/wp-includes/cron.php src/wp-includes/cron.php
    index ffc78728ff..9434822bf4 100644
     
    2121 * Use wp_schedule_event() to schedule a recurring event.
    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
    2728 *
    function wp_schedule_single_event( $timestamp, $hook, $args = array() ) { 
    3637                return false;
    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,
    4842                'timestamp' => $timestamp,
    function wp_schedule_single_event( $timestamp, $hook, $args = array() ) { 
    5044                'args'      => $args,
    5145        );
    5246
     47        /**
     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 {
     68         *     An object containing an event's data.
     69         *
     70         *     @type string       $hook      Action hook to execute when the event is run.
     71         *     @type int          $timestamp Unix timestamp (UTC) for when to next run the event.
     72         *     @type string|false $schedule  How often the event should subsequently recur.
     73         *     @type array        $args      Array containing each separate argument to pass to the hook's callback function.
     74         *     @type int          $interval  The interval time in seconds for the schedule. Only present for recurring events.
     75         * }
     76         */
     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
    5388        /**
    5489         * Filters a single event before it is scheduled.
    5590         *
    function wp_schedule_single_event( $timestamp, $hook, $args = array() ) { 
    74109
    75110        $key = md5( serialize( $event->args ) );
    76111
     112        $crons = _get_cron_array();
    77113        $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
    78114                'schedule' => $event->schedule,
    79115                'args'     => $event->args,
    function wp_schedule_single_event( $timestamp, $hook, $args = array() ) { 
    101137 * Use wp_schedule_single_event() to schedule a non-recurring event.
    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
    107144 *
    function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array() ) { 
    117154                return false;
    118155        }
    119156
    120         $crons     = _get_cron_array();
    121157        $schedules = wp_get_schedules();
    122158
    123159        if ( ! isset( $schedules[ $recurrence ] ) ) {
    function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array() ) { 
    131167                'args'      => $args,
    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 );
    136179
    function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array() ) { 
    141184
    142185        $key = md5( serialize( $event->args ) );
    143186
     187        $crons = _get_cron_array();
    144188        $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
    145189                'schedule' => $event->schedule,
    146190                'args'     => $event->args,
    function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array() ) { 
    154198 * Reschedules a recurring event.
    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.
    160205 * @param string $recurrence How often the event should subsequently recur. See wp_get_schedules() for accepted values.
    function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) 
    168213                return false;
    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'];
     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                }
    183230        }
     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 ) {
    186269                return false;
    function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) 
    204287 * identified.
    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.
    210294 * @param string $hook      Action hook of the event.
    function wp_unschedule_event( $timestamp, $hook, $args = array() ) { 
    219303                return false;
    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 ) );
    224329        unset( $crons[ $timestamp ][ $hook ][ $key ] );
    function wp_unschedule_event( $timestamp, $hook, $args = array() ) { 
    240345 * the `===` operator for testing the return value of this function.
    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.
    246352 * @param array $args Optional. Arguments that were to be passed to the hook's callback function.
    function wp_clear_scheduled_hook( $hook, $args = array() ) { 
    256362                $args = array_slice( func_get_args(), 1 );
    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,
    261388        // and, wp_next_scheduled() returns the same schedule in an infinite loop.
    function wp_clear_scheduled_hook( $hook, $args = array() ) { 
    295422 *                  events were registered on the hook), false if unscheduling fails.
    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 ) ) {
    300447                return 0;
    function wp_unschedule_hook( $hook ) { 
    325472        return false;
    326473}
    327474
     475/**
     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
    328544/**
    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.
    334551 * @param array  $args Optional. Array containing each separate argument to pass to the hook's callback function.
    function wp_unschedule_hook( $hook ) { 
    337554 * @return false|int The Unix timestamp of the next time the event will occur. False if the event doesn't exist.
    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
    353601/**
    function wp_get_schedules() { 
    572820 * @see wp_get_schedules() for available schedules.
    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.
    577826 * @param array $args Optional. Arguments passed to the event's callback function.
    578827 * @return string|false False, if no schedule. Schedule name on success.
    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                 }
     830        $schedule = false;
     831        $event    = wp_get_scheduled_event( $hook, $args );
     832
     833        if ( $event ) {
     834                $schedule = $event->schedule;
    590835        }
    591         return false;
     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
    594849//
  • tests/phpunit/tests/cron.php

    diff --git tests/phpunit/tests/cron.php tests/phpunit/tests/cron.php
    index 26ba8681d2..8676371361 100644
     
    66 * @group cron
    77 */
    88class Tests_Cron extends WP_UnitTestCase {
     9        /**
     10         * @var array Cron array for testing preflight filters.
     11         */
     12        private $preflight_cron_array;
     13
     14        /**
     15         * @var int Timestamp of now() + 30 minutes;
     16         */
     17        private $plus_thirty_minutes;
     18
    919        function setUp() {
    1020                parent::setUp();
    1121                // make sure the schedule is clear
    1222                _set_cron_array( array() );
     23                $this->preflight_cron_array = array();
     24                $this->plus_thirty_minutes = strtotime( '+30 minutes' );
    1325        }
    1426
    1527        function tearDown() {
    class Tests_Cron extends WP_UnitTestCase { 
    306318                // following event should be there too
    307319                $this->assertEquals( $ts2, wp_next_scheduled( $hook, $args ) );
    308320        }
     321
     322        /**
     323         * Ensure the pre_scheduled_event filter prevents
     324         * modification of the cron_array_option.
     325         *
     326         * @ticket 32656
     327         */
     328        function test_pre_schedule_event_filter() {
     329                $hook = __FUNCTION__;
     330                $args = array( 'arg1' );
     331                $ts1  = strtotime( '+30 minutes' );
     332                $ts2  = strtotime( '+3 minutes' );
     333
     334                $expected = _get_cron_array();
     335
     336                add_filter( 'pre_schedule_event', array( $this, '_filter_pre_schedule_event_filter' ), 10, 2 );
     337
     338                $this->assertTrue( wp_schedule_single_event( $ts1, $hook, $args ) );
     339                $this->assertTrue( wp_schedule_event( $ts2, 'hourly', $hook ) );
     340
     341                // Check cron option is unchanged.
     342                $this->assertSame( $expected, _get_cron_array() );
     343
     344                $expected_preflight[ $ts2 ][ $hook ][ md5( serialize( array() ) ) ] = array(
     345                        'schedule' => 'hourly',
     346                        'interval' => HOUR_IN_SECONDS,
     347                        'args'     => array(),
     348                );
     349
     350                $expected_preflight[ $ts1 ][ $hook ][ md5( serialize( $args ) ) ] = array(
     351                        'schedule' => false,
     352                        'interval' => 0,
     353                        'args'     => $args,
     354                );
     355
     356                $this->assertSame( $expected_preflight, $this->preflight_cron_array );
     357        }
     358
     359        /**
     360         * Filter the scheduling of events to use the preflight array.
     361         */
     362        function _filter_pre_schedule_event_filter( $null, $event ) {
     363                $key = md5( serialize( $event->args ) );
     364
     365                $this->preflight_cron_array[ $event->timestamp ][ $event->hook ][ $key ] = array(
     366                        'schedule' => $event->schedule,
     367                        'interval' => isset( $event->interval ) ? $event->interval : 0,
     368                        'args'     => $event->args,
     369                );
     370                uksort( $this->preflight_cron_array, 'strnatcasecmp' );
     371                return true;
     372        }
     373
     374        /**
     375         * Ensure the pre_reschedule_event filter prevents
     376         * modification of the cron_array_option.
     377         *
     378         * @ticket 32656
     379         */
     380        function test_pre_reschedule_event_filter() {
     381                $hook = __FUNCTION__;
     382                $ts1  = strtotime( '+30 minutes' );
     383
     384                // Add an event
     385                $this->assertTrue( wp_schedule_event( $ts1, 'hourly', $hook ) );
     386                $expected = _get_cron_array();
     387
     388                // Add preflight filter.
     389                add_filter( 'pre_reschedule_event', '__return_true' );
     390
     391                // Reschedule event with preflight filter in place.
     392                wp_reschedule_event( $ts1, 'daily', $hook );
     393
     394                // Check cron option is unchanged.
     395                $this->assertSame( $expected, _get_cron_array() );
     396        }
     397
     398        /**
     399         * Ensure the pre_unschedule_event filter prevents
     400         * modification of the cron_array_option.
     401         *
     402         * @ticket 32656
     403         */
     404        function test_pre_unschedule_event_filter() {
     405                $hook = __FUNCTION__;
     406                $ts1  = strtotime( '+30 minutes' );
     407
     408                // Add an event
     409                $this->assertTrue( wp_schedule_event( $ts1, 'hourly', $hook ) );
     410                $expected = _get_cron_array();
     411
     412                // Add preflight filter.
     413                add_filter( 'pre_unschedule_event', '__return_true' );
     414
     415                // Unschedule event with preflight filter in place.
     416                wp_unschedule_event( $ts1, $hook );
     417
     418                // Check cron option is unchanged.
     419                $this->assertSame( $expected, _get_cron_array() );
     420        }
     421
     422        /**
     423         * Ensure the clearing scheduled hooks filter prevents
     424         * modification of the cron_array_option.
     425         *
     426         * @ticket 32656
     427         */
     428        function test_pre_clear_scheduled_hook_filters() {
     429                $hook = __FUNCTION__;
     430                $ts1  = strtotime( '+30 minutes' );
     431
     432                // Add an event
     433                $this->assertTrue( wp_schedule_event( $ts1, 'hourly', $hook ) );
     434                $expected = _get_cron_array();
     435
     436                // Add preflight filters.
     437                add_filter( 'pre_clear_scheduled_hook', '__return_true' );
     438                add_filter( 'pre_unschedule_hook', '__return_zero' );
     439
     440                // Unschedule event with preflight filter in place.
     441                wp_clear_scheduled_hook( $hook );
     442
     443                // Check cron option is unchanged.
     444                $this->assertSame( $expected, _get_cron_array() );
     445
     446                // Unschedule all events with preflight filter in place.
     447                wp_unschedule_hook( $hook );
     448
     449                // Check cron option is unchanged.
     450                $this->assertSame( $expected, _get_cron_array() );
     451        }
     452
     453        /**
     454         * Ensure the preflight hooks for scheduled events
     455         * return a filtered value as expected.
     456         *
     457         * @ticket 32656
     458         */
     459        function test_pre_scheduled_event_hooks() {
     460                add_filter( 'pre_get_scheduled_event', array( $this, 'filter_pre_scheduled_event_hooks' ) );
     461                add_filter( 'pre_next_scheduled', array( $this, 'filter_pre_scheduled_event_hooks' ) );
     462
     463                $actual = wp_get_scheduled_event( 'preflight_event', array(), $this->plus_thirty_minutes );
     464                $actual2 = wp_next_scheduled( 'preflight_event', array() );
     465
     466                $expected = (object) array(
     467                        'hook'      => 'preflight_event',
     468                        'timestamp' => $this->plus_thirty_minutes,
     469                        'schedule'  => false,
     470                        'args'      => array(),
     471                );
     472
     473                $this->assertEquals( $expected, $actual );
     474                $this->assertEquals( $expected, $actual2 );
     475        }
     476
     477        function filter_pre_scheduled_event_hooks() {
     478                return (object) array(
     479                        'hook'      => 'preflight_event',
     480                        'timestamp' => $this->plus_thirty_minutes,
     481                        'schedule'  => false,
     482                        'args'      => array(),
     483                );
     484        }
    309485}