From a091693f9a31e3072900013dfd255838d1f6cd96 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 29 Jul 2022 03:32:58 +0000 Subject: [PATCH] Cron API: Modify `_get_cron_array()` to always return an array. Change the return type of `_get_cron_array()` to an empty array if the `cron` option is either missing or of an unexpected type. This change ensures the return value for no registered events is consistently an empty array. Previously the return value could be either an empty array or `false`. Props thakkarhardik, jrf, costdev. Fixes #53940. git-svn-id: https://develop.svn.wordpress.org/trunk@53791 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/cron.php | 14 +- .../phpunit/tests/admin/wpMediaListTable.php | 4 +- tests/phpunit/tests/cron.php | 6 +- tests/phpunit/tests/cron/getCronArray.php | 120 ++++++++++++++++++ 4 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 tests/phpunit/tests/cron/getCronArray.php diff --git a/src/wp-includes/cron.php b/src/wp-includes/cron.php index 83cbb8b9d0..1577dab9a6 100644 --- a/src/wp-includes/cron.php +++ b/src/wp-includes/cron.php @@ -119,9 +119,6 @@ function wp_schedule_single_event( $timestamp, $hook, $args = array(), $wp_error * are considered duplicates. */ $crons = _get_cron_array(); - if ( ! is_array( $crons ) ) { - $crons = array(); - } $key = md5( serialize( $event->args ) ); $duplicate = false; @@ -306,9 +303,6 @@ function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array(), $wp $key = md5( serialize( $event->args ) ); $crons = _get_cron_array(); - if ( ! is_array( $crons ) ) { - $crons = array(); - } $crons[ $event->timestamp ][ $event->hook ][ $key ] = array( 'schedule' => $event->schedule, @@ -1133,9 +1127,6 @@ function wp_get_ready_cron_jobs() { } $crons = _get_cron_array(); - if ( ! is_array( $crons ) ) { - return array(); - } $gmt_time = microtime( true ); $keys = array_keys( $crons ); @@ -1162,14 +1153,15 @@ function wp_get_ready_cron_jobs() { * Retrieve cron info array option. * * @since 2.1.0 + * @since 6.1.0 Return type modified to consistenty return an array. * @access private * - * @return array[]|false Array of cron info arrays on success, false on failure. + * @return array[] Array of cron events. */ function _get_cron_array() { $cron = get_option( 'cron' ); if ( ! is_array( $cron ) ) { - return false; + return array(); } if ( ! isset( $cron['version'] ) ) { diff --git a/tests/phpunit/tests/admin/wpMediaListTable.php b/tests/phpunit/tests/admin/wpMediaListTable.php index 6a2b2d64fa..a130a32506 100644 --- a/tests/phpunit/tests/admin/wpMediaListTable.php +++ b/tests/phpunit/tests/admin/wpMediaListTable.php @@ -24,6 +24,7 @@ class Tests_Admin_wpMediaListTable extends WP_UnitTestCase { * * @ticket 53949 * @covers WP_Media_List_Table::prepare_items + * @group cron */ public function test_prepare_items_without_cron_option_does_not_throw_warning() { global $wp_query; @@ -42,7 +43,8 @@ class Tests_Admin_wpMediaListTable extends WP_UnitTestCase { delete_option( 'cron' ); // Verify that the cause of the error is in place. - $this->assertFalse( _get_cron_array(), '_get_cron_array() does not return false' ); + $this->assertIsArray( _get_cron_array(), '_get_cron_array() does not return an array.' ); + $this->assertEmpty( _get_cron_array(), '_get_cron_array() does not return an empty array.' ); // If this test does not error out due to the PHP warning, we're good. $mock->prepare_items(); diff --git a/tests/phpunit/tests/cron.php b/tests/phpunit/tests/cron.php index 44e88779da..ebb709fec9 100644 --- a/tests/phpunit/tests/cron.php +++ b/tests/phpunit/tests/cron.php @@ -130,7 +130,8 @@ class Tests_Cron extends WP_UnitTestCase { delete_option( 'cron' ); // Verify that the cause of the error is in place. - $this->assertFalse( _get_cron_array(), '_get_cron_array() does not return false' ); + $this->assertIsArray( _get_cron_array(), '_get_cron_array() does not return an array.' ); + $this->assertEmpty( _get_cron_array(), '_get_cron_array() does not return an empty array.' ); $hook = __FUNCTION__; $timestamp = strtotime( '+10 minutes' ); @@ -151,7 +152,8 @@ class Tests_Cron extends WP_UnitTestCase { delete_option( 'cron' ); // Verify that the cause of the error is in place. - $this->assertFalse( _get_cron_array(), '_get_cron_array() does not return false' ); + $this->assertIsArray( _get_cron_array(), '_get_cron_array() does not return an array.' ); + $this->assertEmpty( _get_cron_array(), '_get_cron_array() does not return an empty array.' ); $hook = __FUNCTION__; $timestamp = strtotime( '+10 minutes' ); diff --git a/tests/phpunit/tests/cron/getCronArray.php b/tests/phpunit/tests/cron/getCronArray.php new file mode 100644 index 0000000000..24ec1c4724 --- /dev/null +++ b/tests/phpunit/tests/cron/getCronArray.php @@ -0,0 +1,120 @@ +assertIsArray( $crons, 'Cron jobs is not an array.' ); + $this->assertCount( 0, $crons, 'Cron job does not contain the expected number of entries.' ); + } + + /** + * Tests the output validation for the `_get_cron_array()` function. + * + * @ticket 53940 + * + * @dataProvider data_get_cron_array_output_validation + * + * @param mixed $input Cron "array". + * @param int $expected Expected array entry count of the cron option after update. + */ + public function test_get_cron_array_output_validation( $input, $expected ) { + update_option( 'cron', $input ); + + $crons = _get_cron_array(); + $this->assertIsArray( $crons, 'Cron jobs is not an array.' ); + $this->assertCount( $expected, $crons, 'Cron job does not contain the expected number of entries.' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_cron_array_output_validation() { + return array( + 'stdClass' => array( + 'input' => new stdClass(), + 'expected' => 0, + ), + 'null' => array( + 'input' => null, + 'expected' => 0, + ), + 'false' => array( + 'input' => false, + 'expected' => 0, + ), + 'true' => array( + 'input' => true, + 'expected' => 0, + ), + 'integer' => array( + 'input' => 53940, + 'expected' => 0, + ), + 'float' => array( + 'input' => 539.40, + 'expected' => 0, + ), + 'string' => array( + 'input' => 'ticket 53940', + 'expected' => 0, + ), + 'empty array' => array( + 'input' => array(), + 'expected' => 0, + ), + 'cron array' => array( + 'input' => array( + 'version' => 2, + time() => array( + 'hookname' => array( + 'event key' => array( + 'schedule' => 'schedule', + 'args' => 'args', + 'interval' => 'interval', + ), + ), + ), + ), + 'expected' => 1, + ), + 'cron v1' => array( + 'input' => array( + time() => array( + 'hookname' => array( + 'args' => 'args', + ), + ), + ), + 'expected' => 1, + ), + ); + } +}