Make WordPress Core


Ignore:
Timestamp:
07/27/2018 02:22:50 AM (5 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/tests/phpunit/tests/cron.php

    r43050 r43540  
    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
     
    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}
Note: See TracChangeset for help on using the changeset viewer.