Changeset 43540
- Timestamp:
- 07/27/2018 02:22:50 AM (7 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/cron.php
r43050 r43540 22 22 * 23 23 * @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. 25 26 * 26 27 * @link https://codex.wordpress.org/Function_Reference/wp_schedule_single_event … … 37 38 } 38 39 39 // Don't schedule a duplicate if there's already an identical event due within 10 minutes of it40 $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();46 40 $event = (object) array( 47 41 'hook' => $hook, … … 52 46 53 47 /** 54 * Filters a single event before it is scheduled. 55 * 56 * @since 3.1.0 57 * 58 * @param stdClass $event { 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 { 59 68 * An object containing an event's data. 60 69 * … … 66 75 * } 67 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 88 /** 89 * Filters a single event before it is scheduled. 90 * 91 * @since 3.1.0 92 * 93 * @param stdClass $event { 94 * An object containing an event's data. 95 * 96 * @type string $hook Action hook to execute when the event is run. 97 * @type int $timestamp Unix timestamp (UTC) for when to next run the event. 98 * @type string|false $schedule How often the event should subsequently recur. 99 * @type array $args Array containing each separate argument to pass to the hook's callback function. 100 * @type int $interval The interval time in seconds for the schedule. Only present for recurring events. 101 * } 102 */ 68 103 $event = apply_filters( 'schedule_event', $event ); 69 104 … … 75 110 $key = md5( serialize( $event->args ) ); 76 111 112 $crons = _get_cron_array(); 77 113 $crons[ $event->timestamp ][ $event->hook ][ $key ] = array( 78 114 'schedule' => $event->schedule, … … 102 138 * 103 139 * @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. 105 142 * 106 143 * @link https://codex.wordpress.org/Function_Reference/wp_schedule_event … … 118 155 } 119 156 120 $crons = _get_cron_array();121 157 $schedules = wp_get_schedules(); 122 158 … … 132 168 'interval' => $schedules[ $recurrence ]['interval'], 133 169 ); 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 134 177 /** This filter is documented in wp-includes/cron.php */ 135 178 $event = apply_filters( 'schedule_event', $event ); … … 142 185 $key = md5( serialize( $event->args ) ); 143 186 187 $crons = _get_cron_array(); 144 188 $crons[ $event->timestamp ][ $event->hook ][ $key ] = array( 145 189 'schedule' => $event->schedule, … … 155 199 * 156 200 * @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. 158 203 * 159 204 * @param int $timestamp Unix timestamp (UTC) for when to next run the event. … … 169 214 } 170 215 171 $crons = _get_cron_array();172 216 $schedules = wp_get_schedules(); 173 $key = md5( serialize( $args ) );174 217 $interval = 0; 175 218 176 // First we try to get it from the schedule219 // First we try to get the interval from the schedule. 177 220 if ( isset( $schedules[ $recurrence ] ) ) { 178 221 $interval = $schedules[ $recurrence ]['interval']; 179 222 } 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']; 183 } 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 } 230 } 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 184 267 // Now we assume something is wrong and fail to schedule 185 268 if ( 0 == $interval ) { … … 205 288 * 206 289 * @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. 208 292 * 209 293 * @param int $timestamp Unix timestamp (UTC) of the event. … … 220 304 } 221 305 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 222 327 $crons = _get_cron_array(); 223 328 $key = md5( serialize( $args ) ); … … 241 346 * 242 347 * @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. 244 350 * 245 351 * @param string $hook Action hook, the execution of which will be unscheduled. … … 257 363 } 258 364 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 259 386 // This logic duplicates wp_next_scheduled() 260 387 // It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing, … … 296 423 */ 297 424 function 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 298 445 $crons = _get_cron_array(); 299 446 if ( empty( $crons ) ) { … … 327 474 328 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 */ 489 function 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 544 /** 329 545 * Retrieve the next timestamp for an event. 330 546 * 331 547 * @since 2.1.0 548 * @since 5.0.0 {@see pre_next_scheduled} and {@see next_scheduled} filters added. 332 549 * 333 550 * @param string $hook Action hook of the event. … … 338 555 */ 339 556 function 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 340 576 $crons = _get_cron_array(); 341 577 $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 } 348 586 } 349 587 } 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 ); 351 599 } 352 600 … … 573 821 * 574 822 * @since 2.1.0 823 * @since 5.0.0 {@see get_schedule} filter added. 575 824 * 576 825 * @param string $hook Action hook to identify the event. … … 579 828 */ 580 829 function 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 } 590 } 591 return false; 830 $schedule = false; 831 $event = wp_get_scheduled_event( $hook, $args ); 832 833 if ( $event ) { 834 $schedule = $event->schedule; 835 } 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 ); 592 847 } 593 848 -
trunk/tests/phpunit/tests/cron.php
r43050 r43540 7 7 */ 8 8 class 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 9 19 function setUp() { 10 20 parent::setUp(); 11 21 // make sure the schedule is clear 12 22 _set_cron_array( array() ); 23 $this->preflight_cron_array = array(); 24 $this->plus_thirty_minutes = strtotime( '+30 minutes' ); 13 25 } 14 26 … … 307 319 $this->assertEquals( $ts2, wp_next_scheduled( $hook, $args ) ); 308 320 } 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 } 309 485 }
Note: See TracChangeset
for help on using the changeset viewer.