Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions features/check-cron-duplicates.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ Feature: Check for excess duplicate cron entries
| cron-duplicates | success | All cron job counts are within normal operating expectations. |
And STDERR should be empty

Scenario: Cron check is healthy when same hook has unique args (e.g. publish_future_post)
Given a wp-content/mu-plugins/plugin.php file:
"""
<?php
for ( $i = 1; $i <= 15; $i++ ) {
wp_schedule_single_event( time() + ( $i * 60 ), 'publish_future_post', array( $i ) );
}
"""

When I run `wp doctor check cron-duplicates`
Then STDOUT should be a table containing rows:
| name | status | message |
| cron-duplicates | success | All cron job counts are within normal operating expectations. |
And STDERR should be empty

Scenario: Cron check errors with excess duplicate crons
Given a wp-content/mu-plugins/plugin.php file:
"""
Expand Down
8 changes: 7 additions & 1 deletion src/Check/Cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ protected static function get_crons() {
}

ob_start();
WP_CLI::run_command( array( 'cron', 'event', 'list' ), array( 'format' => 'json' ) );
WP_CLI::run_command(
array( 'cron', 'event', 'list' ),
array(
'format' => 'json',
'fields' => 'hook,args',
)
);
Comment on lines +21 to +25
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

Cron::get_crons() is shared by Cron_Count and Cron_Duplicates. Requesting args here means the cron-count check now retrieves/serializes args for every event even though it only calls count($crons), which could be a performance/memory regression on sites with large/complex args. Consider keeping get_crons() lightweight and only requesting args for the duplicates check (e.g., accept a $fields parameter and cache per field-set, or add a dedicated method used only by Cron_Duplicates).

Copilot uses AI. Check for mistakes.
$ret = ob_get_clean();
self::$crons = ! empty( $ret ) ? json_decode( $ret, true ) : array();
return self::$crons;
Expand Down
18 changes: 14 additions & 4 deletions src/Check/Cron_Duplicates.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@ public function run() {
$job_counts = array();
$excess_duplicates = false;
foreach ( $crons as $job ) {
if ( ! isset( $job_counts[ $job['hook'] ] ) ) {
$job_counts[ $job['hook'] ] = 0;
$key_data = array( $job['hook'], isset( $job['args'] ) ? $job['args'] : array() );
if ( function_exists( 'wp_json_encode' ) ) {
$key = wp_json_encode( $key_data );
} else {
$key = json_encode( $key_data );
}
++$job_counts[ $job['hook'] ];
if ( $job_counts[ $job['hook'] ] >= $this->threshold_count ) {
if ( false === $key ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
$key = serialize( $key_data );
}
if ( ! isset( $job_counts[ $key ] ) ) {
$job_counts[ $key ] = 0;
}
++$job_counts[ $key ];
if ( $job_counts[ $key ] >= $this->threshold_count ) {
$excess_duplicates = true;
}
}
Expand Down