WordPress.org

Make WordPress Core

Ticket #34913: 0002-a-Fix-issue-34913.patch

File 0002-a-Fix-issue-34913.patch, 10.0 KB (added by jrf, 4 years ago)

Updated for bug scrub feedback

  • src/wp-admin/includes/upgrade.php

    From 1c26d9a943f0550b87106def2bd947170227562a Mon Sep 17 00:00:00 2001
    From: jrfnl <github_nospam@adviesenzo.nl>
    Date: Sat, 13 Feb 2016 05:36:43 +0100
    Subject: [PATCH] Fix issue #34913 "Unscheduling cron jobs fails when original
     arguments were not an array."
    
    ---
     src/wp-admin/includes/upgrade.php |  6 ++-
     src/wp-includes/cron.php          | 84 +++++++++++++++++++++++-----------
     tests/phpunit/tests/cron.php      | 96 +++++++++++++++++++++++++++++++++++++++
     3 files changed, 158 insertions(+), 28 deletions(-)
    
    diff --git a/src/wp-admin/includes/upgrade.php b/src/wp-admin/includes/upgrade.php
    index 07de246..55041c3 100644
    a b function upgrade_440() { 
    16721672 */
    16731673function upgrade_450() {
    16741674        global $wp_current_db_version;
    1675         if ( $wp_current_db_version < 36180 )
     1675        if ( $wp_current_db_version < 36180 ) {
    16761676                wp_clear_scheduled_hook( 'wp_maybe_auto_update' );
     1677        }
     1678
     1679        // Getting the cron array will automatically update it to version 3.
     1680        _get_cron_array();
    16771681}
    16781682
    16791683/**
  • src/wp-includes/cron.php

    diff --git a/src/wp-includes/cron.php b/src/wp-includes/cron.php
    index 493e0ec..58633e5 100644
    a b function wp_schedule_single_event( $timestamp, $hook, $args = array()) { 
    4747        if ( ! $event )
    4848                return false;
    4949
     50        $event->args = _cron_cast_to_array_helper( $event->args );
    5051        $key = md5(serialize($event->args));
    5152
    5253        $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args );
    function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) { 
    9495        if ( ! $event )
    9596                return false;
    9697
     98        $event->args = _cron_cast_to_array_helper( $event->args );
    9799        $key = md5(serialize($event->args));
    98100
    99101        $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args, 'interval' => $event->interval );
    function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) 
    120122
    121123        $crons = _get_cron_array();
    122124        $schedules = wp_get_schedules();
    123         $key = md5( serialize( $args ) );
    124125        $interval = 0;
    125126
    126127        // First we try to get it from the schedule
    function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) 
    129130        }
    130131        // Now we try to get it from the saved interval in case the schedule disappears
    131132        if ( 0 == $interval ) {
     133                $key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
    132134                $interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
    133135        }
    134136        // Now we assume something is wrong and fail to schedule
    function wp_unschedule_event( $timestamp, $hook, $args = array() ) { 
    170172        }
    171173
    172174        $crons = _get_cron_array();
    173         $key = md5(serialize($args));
    174         unset( $crons[$timestamp][$hook][$key] );
    175         if ( empty($crons[$timestamp][$hook]) )
    176                 unset( $crons[$timestamp][$hook] );
    177         if ( empty($crons[$timestamp]) )
    178                 unset( $crons[$timestamp] );
     175        $key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
     176        unset( $crons[ $timestamp ][ $hook ][ $key ] );
     177        if ( empty( $crons[ $timestamp ][ $hook ] ) ) {
     178                unset( $crons[ $timestamp ][ $hook ] );
     179        }
     180        if ( empty( $crons[ $timestamp ] ) ) {
     181                unset( $crons[ $timestamp ] );
     182        }
    179183        _set_cron_array( $crons );
    180184}
    181185
    function wp_clear_scheduled_hook( $hook, $args = array() ) { 
    221225 */
    222226function wp_next_scheduled( $hook, $args = array() ) {
    223227        $crons = _get_cron_array();
    224         $key = md5(serialize($args));
    225         if ( empty($crons) )
     228        if ( empty( $crons ) ) {
    226229                return false;
     230        }
     231
     232        $key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
    227233        foreach ( $crons as $timestamp => $cron ) {
    228                 if ( isset( $cron[$hook][$key] ) )
     234                if ( isset( $cron[ $hook ][ $key ] ) ) {
    229235                        return $timestamp;
     236                }
    230237        }
    231238        return false;
    232239}
    function wp_get_schedules() { 
    416423 */
    417424function wp_get_schedule($hook, $args = array()) {
    418425        $crons = _get_cron_array();
    419         $key = md5(serialize($args));
    420         if ( empty($crons) )
     426        if ( empty( $crons ) ) {
    421427                return false;
     428        }
     429
     430        $key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
    422431        foreach ( $crons as $timestamp => $cron ) {
    423                 if ( isset( $cron[$hook][$key] ) )
    424                         return $cron[$hook][$key]['schedule'];
     432                if ( isset( $cron[ $hook ][ $key ] ) ) {
     433                        return $cron[ $hook ][ $key ]['schedule'];
     434                }
    425435        }
    426436        return false;
    427437}
    function wp_get_schedule($hook, $args = array()) { 
    439449 * @return false|array CRON info array.
    440450 */
    441451function _get_cron_array()  {
    442         $cron = get_option('cron');
    443         if ( ! is_array($cron) )
     452        $cron = get_option( 'cron' );
     453        if ( ! is_array( $cron ) ) {
    444454                return false;
     455        }
    445456
    446         if ( !isset($cron['version']) )
    447                 $cron = _upgrade_cron_array($cron);
     457        if ( ! isset( $cron['version'] ) || 3 > $cron['version'] ) {
     458                $cron = _upgrade_cron_array( $cron );
     459        }
    448460
    449         unset($cron['version']);
     461        unset( $cron['version'] );
    450462
    451463        return $cron;
    452464}
    function _get_cron_array() { 
    460472 * @param array $cron Cron info array from {@link _get_cron_array()}.
    461473 */
    462474function _set_cron_array($cron) {
    463         $cron['version'] = 2;
     475        $cron['version'] = 3;
    464476        update_option( 'cron', $cron );
    465477}
    466478
    467479/**
    468480 * Upgrade a Cron info array.
    469481 *
    470  * This function upgrades the Cron info array to version 2.
     482 * This function upgrades the Cron info array to version 3.
    471483 *
    472484 * @since 2.1.0
    473485 * @access private
    function _set_cron_array($cron) { 
    476488 * @return array An upgraded Cron info array.
    477489 */
    478490function _upgrade_cron_array($cron) {
    479         if ( isset($cron['version']) && 2 == $cron['version'])
     491        if ( isset( $cron['version'] ) && 3 === $cron['version'] ) {
    480492                return $cron;
     493        }
    481494
    482495        $new_cron = array();
    483496
    484         foreach ( (array) $cron as $timestamp => $hooks) {
    485                 foreach ( (array) $hooks as $hook => $args ) {
    486                         $key = md5(serialize($args['args']));
    487                         $new_cron[$timestamp][$hook][$key] = $args;
     497        foreach ( (array) $cron as $timestamp => $hooks ) {
     498                foreach ( (array) $hooks as $hook => $event ) {
     499                        foreach( (array) $event as $args ) {
     500                                $key = md5( serialize( _cron_cast_to_array_helper( $args['args'] ) ) );
     501                                $new_cron[ $timestamp ][ $hook ][ $key ] = $args;
     502                        }
    488503                }
    489504        }
    490505
    491         $new_cron['version'] = 2;
     506        $new_cron['version'] = 3;
    492507        update_option( 'cron', $new_cron );
    493508        return $new_cron;
    494509}
     510
     511/**
     512 * Compatibility function for consistent casting to array of cron arguments.
     513 *
     514 * @since 4.5.0
     515 *
     516 * @param mixed $args Cron arguments.
     517 * @return array
     518 */
     519function _cron_cast_to_array_helper( $args ) {
     520        if ( is_object( $args ) ) {
     521                return array( $args );
     522        }
     523        return (array) $args;
     524}
     525 No newline at end of file
  • tests/phpunit/tests/cron.php

    diff --git a/tests/phpunit/tests/cron.php b/tests/phpunit/tests/cron.php
    index b411a5b..bc25eb0 100644
    a b class Tests_Cron extends WP_UnitTestCase { 
    189189
    190190        }
    191191
     192
     193        /**
     194         * @ticket 34913
     195         *
     196         * @expectedDeprecated wp_clear_scheduled_hook
     197         */
     198        function test_clear_schedule_non_array_args() {
     199                $hook = rand_str();
     200                $bool_arg = true;
     201                $int_arg = mt_rand();
     202                $float_arg = mt_rand() / mt_getrandmax();
     203                $string_arg = rand_str();
     204                $obj_arg = new cronTestClass();
     205
     206                // Schedule several events with non-array arguments.
     207                wp_schedule_single_event( strtotime('+1 hour'), $hook, $bool_arg );
     208                wp_schedule_single_event( strtotime('+2 hour'), $hook, $int_arg );
     209                wp_schedule_single_event( strtotime('+3 hour'), $hook, $float_arg );
     210                wp_schedule_single_event( strtotime('+4 hour'), $hook, $string_arg );
     211                wp_schedule_single_event( strtotime('+5 hour'), $hook, $obj_arg );
     212
     213                // Make sure they're returned by wp_next_scheduled().
     214                $this->assertTrue( wp_next_scheduled($hook, $bool_arg) > 0 );
     215                $this->assertTrue( wp_next_scheduled($hook, $int_arg) > 0 );
     216                $this->assertTrue( wp_next_scheduled($hook, $float_arg) > 0 );
     217                $this->assertTrue( wp_next_scheduled($hook, $string_arg) > 0 );
     218                $this->assertTrue( wp_next_scheduled($hook, $obj_arg) > 0 );
     219
     220                // Clear the schedule for the bool_arg event and make sure it's gone.
     221                wp_clear_scheduled_hook($hook, $bool_arg );
     222                $this->assertFalse( wp_next_scheduled($hook, $bool_arg) );
     223
     224                // Clear the schedule for the int_arg event and make sure it's gone.
     225                wp_clear_scheduled_hook($hook, $int_arg );
     226                $this->assertFalse( wp_next_scheduled($hook, $int_arg) );
     227
     228                // Clear the schedule for the float_arg event and make sure it's gone.
     229                wp_clear_scheduled_hook($hook, $float_arg );
     230                $this->assertFalse( wp_next_scheduled($hook, $float_arg) );
     231
     232                // Clear the schedule for the string_arg event and make sure it's gone.
     233                wp_clear_scheduled_hook($hook, $string_arg );
     234                $this->assertFalse( wp_next_scheduled($hook, $string_arg) );
     235
     236                // Clear the schedule for the object_arg event and make sure it's gone.
     237                wp_clear_scheduled_hook($hook, $obj_arg );
     238                $this->assertFalse( wp_next_scheduled($hook, $obj_arg) );
     239
     240        }
     241
     242        /**
     243         * @ticket 34913
     244         *
     245         * {@internal Separate test for resources as this one can easily fail to create
     246         * the necessary resource and we don't want to skip the other wp_clear_scheduled_hook()
     247         * tests if it does.}}
     248         *
     249         * @expectedDeprecated wp_clear_scheduled_hook
     250         */
     251        function test_clear_schedule_resource_arg() {
     252                $hook = rand_str();
     253                $arg = tmpfile();
     254
     255                if ( $arg === false ) {
     256                        $this->markTestSkipped( 'Could not create resource.' );
     257                        return;
     258                }
     259
     260                // Schedule event with resource argument.
     261                wp_schedule_single_event( strtotime('+1 hour'), $hook, $arg );
     262
     263                // Make sure they're returned by wp_next_scheduled().
     264                $this->assertTrue( wp_next_scheduled($hook, $arg) > 0 );
     265
     266                // Clear the schedule for the event and make sure it's gone.
     267                wp_clear_scheduled_hook($hook, $arg );
     268                $this->assertFalse( wp_next_scheduled($hook, $arg) );
     269               
     270                fclose( $arg );
     271
     272        }
     273
     274
    192275        /**
    193276         * @ticket 6966
    194277         */
    class WPTestCronRunning extends _WPEmptyBlog { 
    327410        }
    328411}
    329412*/
     413
     414/**
     415 * Test class belonging to the `test_clear_schedule_non_array_arg()` test.
     416 */
     417class cronTestClass {
     418        public $property_a = 'something';
     419        public $property_b = 123;
     420        private $property_c = false;
     421
     422        public function some_function() {
     423                return true;
     424        }
     425}