Skip to content

Handle bad interval for cron schedule#10619

Open
westonruter wants to merge 12 commits intoWordPress:trunkfrom
westonruter:trac-64404
Open

Handle bad interval for cron schedule#10619
westonruter wants to merge 12 commits intoWordPress:trunkfrom
westonruter:trac-64404

Conversation

@westonruter
Copy link
Copy Markdown
Member

Trac ticket: https://core.trac.wordpress.org/ticket/64404


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions
Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@westonruter westonruter marked this pull request as ready for review December 14, 2025 22:29
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Dec 14, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props westonruter, peterwilsoncc, johnbillion.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment thread src/wp-includes/cron.php Outdated
Comment thread src/wp-includes/cron.php Outdated
__( 'Event schedule does not exist.' )
sprintf(
/* translators: %s is the interval encoded as JSON */
__( 'Event schedule is invalid. Interval must be positive integer, but got: %s' ),
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the $recurrence should be included here as well?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be good if a named schedule is used, ie if the code doesn't fall back to

// Now we try to get it from the saved interval in case the schedule disappears.
if ( 0 === $interval ) {
$scheduled_event = wp_get_scheduled_event( $hook, $args, $timestamp );
if ( $scheduled_event && isset( $scheduled_event->interval ) ) {
$interval = $scheduled_event->interval;
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not like 063c21e?

Copy link
Copy Markdown
Contributor

@peterwilsoncc peterwilsoncc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a few notes inline.

Comment thread src/wp-includes/cron.php Outdated
Comment thread src/wp-includes/cron.php Outdated

// Now we assume something is wrong and fail to schedule.
if ( 0 === $interval ) {
if ( ! is_int( $interval ) || $interval <= 0 ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! is_int( $interval ) || $interval <= 0 ) {
if ( ! is_numeric( $interval ) || $interval <= 0 ) {

Matches the behaivour of the existing check in wp_schedule_event()

Comment thread src/wp-includes/cron.php Outdated
__( 'Event schedule does not exist.' )
sprintf(
/* translators: %s is the interval encoded as JSON */
__( 'Event schedule is invalid. Interval must be positive integer, but got: %s' ),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be good if a named schedule is used, ie if the code doesn't fall back to

// Now we try to get it from the saved interval in case the schedule disappears.
if ( 0 === $interval ) {
$scheduled_event = wp_get_scheduled_event( $hook, $args, $timestamp );
if ( $scheduled_event && isset( $scheduled_event->interval ) ) {
$interval = $scheduled_event->interval;
}
}

@johnbillion
Copy link
Copy Markdown
Member

johnbillion commented Dec 19, 2025

There's a failing test:

1) Tests_Cron::test_invalid_recurrence_for_event_returns_error
sprintf(): Argument number must be greater than zero

/var/www/src/wp-includes/cron.php:428
/var/www/tests/phpunit/tests/cron.php:856
phpvfscomposer:///var/www/vendor/phpunit/phpunit/phpunit:106
/var/www/vendor/bin/phpunit:118

Comment thread src/wp-includes/cron.php Outdated
__( 'Event schedule with recurrence "%$1s" does not exist or has is invalid. Interval must be positive integer, but got: %$2s' ),
$recurrence,
wp_json_encode( $interval )
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a couple of reads to understand what this is trying to tell me. The interval is an internal implementation detail that's not used by anyone calling this function, so I'm not sure it's an actionable message.

Is it possible to reliably distinguish between a schedule that doesn't exist and a schedule that exists but has an invalid interval? If so, let's use two separate error messages, one for each case, then make each error message clearer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about c6efb67?

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds validation to handle invalid intervals for cron schedules, addressing ticket 64404. When a cron schedule is registered with an invalid interval (non-numeric or ≤ 0), the scheduling functions now return an appropriate error instead of allowing invalid data.

Changes:

  • Adds interval validation in wp_schedule_event() and wp_reschedule_event() to check for non-numeric or non-positive values
  • Introduces a new 'invalid_interval' error code to distinguish interval problems from missing schedules
  • Adds comprehensive test coverage for negative interval scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/wp-includes/cron.php Adds validation for invalid intervals in both wp_schedule_event() (lines 319-328) and wp_reschedule_event() (lines 458-474), with appropriate error messages distinguishing between missing schedules and invalid intervals
tests/phpunit/tests/cron.php Adds test_invalid_schedule_interval_returns_error() test case with a schedule containing a negative interval, verifies both functions return the correct error code, and adds ticket reference to related test

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/phpunit/tests/cron.php
Copy link
Copy Markdown

@rbcorrales rbcorrales left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested the PR locally, and left a couple of inline comments on an edge case in the interval validation with a suggestion.

Comment thread src/wp-includes/cron.php
return false;
}

if ( ! is_numeric( $event->interval ) || $event->interval <= 0 ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fractional floats between 0 and 1 (like 0.5, 0.1, 0.99) pass this validation because is_numeric(0.5) is true and 0.5 > 0. But PHP's % operator casts floats to int, so 0.5 becomes 0 and the modulo in wp_reschedule_event() still throws DivisionByZeroError.

Casting to (int) here catches these values at validation time, the same way the arithmetic would truncate them:

Suggested change
if ( ! is_numeric( $event->interval ) || $event->interval <= 0 ) {
if ( ! is_numeric( $event->interval ) || (int) $event->interval <= 0 ) {

I verified it locally without the cast, wp_schedule_event( time(), 'half_second_schedule', ... ) succeeds and wp_reschedule_event() crashes. With the cast, both return invalid_interval correctly.

Comment thread src/wp-includes/cron.php

// Now we assume something is wrong and fail to schedule.
if ( 0 === $interval ) {
if ( ! is_numeric( $interval ) || $interval <= 0 ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion here:

Suggested change
if ( ! is_numeric( $interval ) || $interval <= 0 ) {
if ( ! is_numeric( $interval ) || (int) $interval <= 0 ) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants