---
 src/wp-admin/includes/upgrade.php |   3 ++
 src/wp-includes/cron.php          | 108 ++++++++++++++++++++++++++++----------
 tests/phpunit/tests/cron.php      | 100 +++++++++++++++++++++++++++++++++++
 3 files changed, 184 insertions(+), 27 deletions(-)

diff --git a/src/wp-admin/includes/upgrade.php b/src/wp-admin/includes/upgrade.php
index e09d42a..649847c 100644
--- a/src/wp-admin/includes/upgrade.php
+++ b/src/wp-admin/includes/upgrade.php
@@ -1682,6 +1682,9 @@ function upgrade_450() {
 	if ( $wp_current_db_version < 36679 && is_multisite() ) {
 		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name REGEXP '^[0-9]+_new_email$'" );
 	}
+	
+	// Getting the cron array will automatically update it to version 3.
+	_get_cron_array();
 }
 
 /**
diff --git a/src/wp-includes/cron.php b/src/wp-includes/cron.php
index 493e0ec..e823026 100644
--- a/src/wp-includes/cron.php
+++ b/src/wp-includes/cron.php
@@ -21,6 +21,10 @@
  * @return false|void False when an event is not scheduled.
  */
 function wp_schedule_single_event( $timestamp, $hook, $args = array()) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	// Make sure timestamp is a positive integer
 	if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 		return false;
@@ -47,6 +51,7 @@ function wp_schedule_single_event( $timestamp, $hook, $args = array()) {
 	if ( ! $event )
 		return false;
 
+	$event->args = _cron_cast_to_array_helper( $event->args );
 	$key = md5(serialize($event->args));
 
 	$crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args );
@@ -75,6 +80,10 @@ function wp_schedule_single_event( $timestamp, $hook, $args = array()) {
  * @return false|void False when an event is not scheduled.
  */
 function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	// Make sure timestamp is a positive integer
 	if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 		return false;
@@ -94,6 +103,7 @@ function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
 	if ( ! $event )
 		return false;
 
+	$event->args = _cron_cast_to_array_helper( $event->args );
 	$key = md5(serialize($event->args));
 
 	$crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args, 'interval' => $event->interval );
@@ -113,6 +123,10 @@ function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
  * @return false|void False when an event is not scheduled.
  */
 function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	// Make sure timestamp is a positive integer
 	if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 		return false;
@@ -120,7 +134,6 @@ function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() )
 
 	$crons = _get_cron_array();
 	$schedules = wp_get_schedules();
-	$key = md5( serialize( $args ) );
 	$interval = 0;
 
 	// First we try to get it from the schedule
@@ -129,6 +142,7 @@ function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() )
 	}
 	// Now we try to get it from the saved interval in case the schedule disappears
 	if ( 0 == $interval ) {
+		$key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
 		$interval = $crons[ $timestamp ][ $hook ][ $key ]['interval'];
 	}
 	// Now we assume something is wrong and fail to schedule
@@ -164,18 +178,24 @@ function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() )
  * @return false|void False when an event is not unscheduled.
  */
 function wp_unschedule_event( $timestamp, $hook, $args = array() ) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	// Make sure timestamp is a positive integer
 	if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
 		return false;
 	}
 
 	$crons = _get_cron_array();
-	$key = md5(serialize($args));
-	unset( $crons[$timestamp][$hook][$key] );
-	if ( empty($crons[$timestamp][$hook]) )
-		unset( $crons[$timestamp][$hook] );
-	if ( empty($crons[$timestamp]) )
-		unset( $crons[$timestamp] );
+	$key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
+	unset( $crons[ $timestamp ][ $hook ][ $key ] );
+	if ( empty( $crons[ $timestamp ][ $hook ] ) ) {
+		unset( $crons[ $timestamp ][ $hook ] );
+	}
+	if ( empty( $crons[ $timestamp ] ) ) {
+		unset( $crons[ $timestamp ] );
+	}
 	_set_cron_array( $crons );
 }
 
@@ -220,13 +240,20 @@ function wp_clear_scheduled_hook( $hook, $args = array() ) {
  * @return false|int The UNIX timestamp of the next time the scheduled event will occur.
  */
 function wp_next_scheduled( $hook, $args = array() ) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	$crons = _get_cron_array();
-	$key = md5(serialize($args));
-	if ( empty($crons) )
+	if ( empty( $crons ) ) {
 		return false;
+	}
+
+	$key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
 	foreach ( $crons as $timestamp => $cron ) {
-		if ( isset( $cron[$hook][$key] ) )
+		if ( isset( $cron[ $hook ][ $key ] ) ) {
 			return $timestamp;
+		}
 	}
 	return false;
 }
@@ -415,13 +442,20 @@ function wp_get_schedules() {
  * @return string|false False, if no schedule. Schedule on success.
  */
 function wp_get_schedule($hook, $args = array()) {
+	if ( ! is_array( $args ) ) {
+		_doing_it_wrong( __FUNCTION__, __( 'Cron arguments are expected to be an array. Please update your function call.' ), '2.1.0' );
+	}
+
 	$crons = _get_cron_array();
-	$key = md5(serialize($args));
-	if ( empty($crons) )
+	if ( empty( $crons ) ) {
 		return false;
+	}
+
+	$key = md5( serialize( _cron_cast_to_array_helper( $args ) ) );
 	foreach ( $crons as $timestamp => $cron ) {
-		if ( isset( $cron[$hook][$key] ) )
-			return $cron[$hook][$key]['schedule'];
+		if ( isset( $cron[ $hook ][ $key ] ) ) {
+			return $cron[ $hook ][ $key ]['schedule'];
+		}
 	}
 	return false;
 }
@@ -439,14 +473,16 @@ function wp_get_schedule($hook, $args = array()) {
  * @return false|array CRON info array.
  */
 function _get_cron_array()  {
-	$cron = get_option('cron');
-	if ( ! is_array($cron) )
+	$cron = get_option( 'cron' );
+	if ( ! is_array( $cron ) ) {
 		return false;
+	}
 
-	if ( !isset($cron['version']) )
-		$cron = _upgrade_cron_array($cron);
+	if ( ! isset( $cron['version'] ) || 3 > $cron['version'] ) {
+		$cron = _upgrade_cron_array( $cron );
+	}
 
-	unset($cron['version']);
+	unset( $cron['version'] );
 
 	return $cron;
 }
@@ -460,14 +496,14 @@ function _get_cron_array()  {
  * @param array $cron Cron info array from {@link _get_cron_array()}.
  */
 function _set_cron_array($cron) {
-	$cron['version'] = 2;
+	$cron['version'] = 3;
 	update_option( 'cron', $cron );
 }
 
 /**
  * Upgrade a Cron info array.
  *
- * This function upgrades the Cron info array to version 2.
+ * This function upgrades the Cron info array to version 3.
  *
  * @since 2.1.0
  * @access private
@@ -476,19 +512,37 @@ function _set_cron_array($cron) {
  * @return array An upgraded Cron info array.
  */
 function _upgrade_cron_array($cron) {
-	if ( isset($cron['version']) && 2 == $cron['version'])
+	if ( isset( $cron['version'] ) && 3 === $cron['version'] ) {
 		return $cron;
+	}
 
 	$new_cron = array();
 
-	foreach ( (array) $cron as $timestamp => $hooks) {
-		foreach ( (array) $hooks as $hook => $args ) {
-			$key = md5(serialize($args['args']));
-			$new_cron[$timestamp][$hook][$key] = $args;
+	foreach ( (array) $cron as $timestamp => $hooks ) {
+		foreach ( (array) $hooks as $hook => $event ) {
+			foreach( (array) $event as $args ) {
+				$key = md5( serialize( _cron_cast_to_array_helper( $args['args'] ) ) );
+				$new_cron[ $timestamp ][ $hook ][ $key ] = $args;
+			}
 		}
 	}
 
-	$new_cron['version'] = 2;
+	$new_cron['version'] = 3;
 	update_option( 'cron', $new_cron );
 	return $new_cron;
 }
+
+/**
+ * Compatibility function for consistent casting to array of cron arguments.
+ *
+ * @since 4.5.0
+ *
+ * @param mixed $args Cron arguments.
+ * @return array
+ */
+function _cron_cast_to_array_helper( $args ) {
+	if ( is_object( $args ) ) {
+		return array( $args );
+	}
+	return (array) $args;
+}
\ No newline at end of file
diff --git a/tests/phpunit/tests/cron.php b/tests/phpunit/tests/cron.php
index b411a5b..390f00c 100644
--- a/tests/phpunit/tests/cron.php
+++ b/tests/phpunit/tests/cron.php
@@ -189,6 +189,93 @@ class Tests_Cron extends WP_UnitTestCase {
 
 	}
 
+
+	/**
+	 * @ticket 34913
+	 *
+	 * @expectedDeprecated wp_clear_scheduled_hook
+	 * @expectedIncorrectUsage wp_schedule_single_event
+	 * @expectedIncorrectUsage wp_next_scheduled
+	 */
+	function test_clear_schedule_non_array_args() {
+		$hook = rand_str();
+		$bool_arg = true;
+		$int_arg = mt_rand();
+		$float_arg = mt_rand() / mt_getrandmax();
+		$string_arg = rand_str();
+		$obj_arg = new cronTestClass();
+
+		// Schedule several events with non-array arguments.
+		wp_schedule_single_event( strtotime('+1 hour'), $hook, $bool_arg );
+		wp_schedule_single_event( strtotime('+2 hour'), $hook, $int_arg );
+		wp_schedule_single_event( strtotime('+3 hour'), $hook, $float_arg );
+		wp_schedule_single_event( strtotime('+4 hour'), $hook, $string_arg );
+		wp_schedule_single_event( strtotime('+5 hour'), $hook, $obj_arg );
+
+		// Make sure they're returned by wp_next_scheduled().
+		$this->assertTrue( wp_next_scheduled($hook, $bool_arg) > 0 );
+		$this->assertTrue( wp_next_scheduled($hook, $int_arg) > 0 );
+		$this->assertTrue( wp_next_scheduled($hook, $float_arg) > 0 );
+		$this->assertTrue( wp_next_scheduled($hook, $string_arg) > 0 );
+		$this->assertTrue( wp_next_scheduled($hook, $obj_arg) > 0 );
+
+		// Clear the schedule for the bool_arg event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $bool_arg );
+		$this->assertFalse( wp_next_scheduled($hook, $bool_arg) );
+
+		// Clear the schedule for the int_arg event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $int_arg );
+		$this->assertFalse( wp_next_scheduled($hook, $int_arg) );
+
+		// Clear the schedule for the float_arg event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $float_arg );
+		$this->assertFalse( wp_next_scheduled($hook, $float_arg) );
+
+		// Clear the schedule for the string_arg event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $string_arg );
+		$this->assertFalse( wp_next_scheduled($hook, $string_arg) );
+
+		// Clear the schedule for the object_arg event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $obj_arg );
+		$this->assertFalse( wp_next_scheduled($hook, $obj_arg) );
+
+	}
+
+	/**
+	 * @ticket 34913
+	 *
+	 * {@internal Separate test for resources as this one can easily fail to create
+	 * the necessary resource and we don't want to skip the other wp_clear_scheduled_hook()
+	 * tests if it does.}}
+	 *
+	 * @expectedDeprecated wp_clear_scheduled_hook
+	 * @expectedIncorrectUsage wp_schedule_single_event
+	 * @expectedIncorrectUsage wp_next_scheduled
+	 */
+	function test_clear_schedule_resource_arg() {
+		$hook = rand_str();
+		$arg = tmpfile();
+
+		if ( $arg === false ) {
+			$this->markTestSkipped( 'Could not create resource.' );
+			return;
+		}
+
+		// Schedule event with resource argument.
+		wp_schedule_single_event( strtotime('+1 hour'), $hook, $arg );
+
+		// Make sure they're returned by wp_next_scheduled().
+		$this->assertTrue( wp_next_scheduled($hook, $arg) > 0 );
+
+		// Clear the schedule for the event and make sure it's gone.
+		wp_clear_scheduled_hook($hook, $arg );
+		$this->assertFalse( wp_next_scheduled($hook, $arg) );
+		
+		fclose( $arg );
+
+	}
+
+
 	/**
 	 * @ticket 6966
 	 */
@@ -327,3 +414,16 @@ class WPTestCronRunning extends _WPEmptyBlog {
 	}
 }
 */
+
+/**
+ * Test class belonging to the `test_clear_schedule_non_array_arg()` test.
+ */
+class cronTestClass {
+	public $property_a = 'something';
+	public $property_b = 123;
+	private $property_c = false;
+
+	public function some_function() {
+		return true;
+	}
+}
-- 
1.9.4.msysgit.2

