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
39 changes: 39 additions & 0 deletions features/cron-event.feature
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,45 @@ Feature: Manage WP Cron events
Debug: Arguments:
"""

Scenario: Run cron events with --network flag on non-multisite
When I try `wp cron event run --due-now --network`
Then STDERR should be:
"""
Error: This is not a multisite installation.
"""
And the return code should be 1

Scenario: Run cron events with --network flag on multisite
Given a WP multisite subdirectory install
And I run `wp site create --slug=site2`
And I run `wp site create --slug=site3`

When I run `wp cron event schedule wp_cli_network_test now`
Then STDOUT should contain:
"""
Success: Scheduled event with hook 'wp_cli_network_test'
"""

When I run `wp --url=example.com/site2 cron event schedule wp_cli_network_test_site2 now`
Then STDOUT should contain:
"""
Success: Scheduled event with hook 'wp_cli_network_test_site2'
"""

When I run `wp cron event run --due-now --network --exclude=wp_privacy_delete_old_export_files,wp_version_check,wp_update_plugins,wp_update_themes,wp_site_health_scheduled_check,wp_update_user_counts,wp_scheduled_delete,wp_scheduled_auto_draft_delete`
Then STDOUT should contain:
"""
Executed the cron event 'wp_cli_network_test'
"""
And STDOUT should contain:
"""
Executed the cron event 'wp_cli_network_test_site2'
"""
And STDOUT should contain:
"""
Success: Executed a total of 2 cron events across 3 sites.
"""

Scenario: List cron events with actions field
Given a wp-content/mu-plugins/test-cron-actions.php file:
"""
Expand Down
120 changes: 107 additions & 13 deletions src/Cron_Event_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,21 +235,98 @@ public function schedule( $args, $assoc_args ) {
* [--all]
* : Run all hooks.
*
* [--network]
* : Run hooks across all sites in a multisite installation.
*
* ## EXAMPLES
*
* # Run all cron events due right now
* $ wp cron event run --due-now
* Executed the cron event 'cron_test_1' in 0.01s.
* Executed the cron event 'cron_test_2' in 0.006s.
* Success: Executed a total of 2 cron events.
*
* # Run all cron events due right now across all sites in a multisite
* $ wp cron event run --due-now --network
* Executed the cron event 'cron_test_1' in 0.01s.
* Executed the cron event 'cron_test_2' in 0.006s.
* Success: Executed a total of 2 cron events across 3 sites.
*/
public function run( $args, $assoc_args ) {
$due_now = Utils\get_flag_value( $assoc_args, 'due-now' );

$network = Utils\get_flag_value( $assoc_args, 'network' );
$lock_timeout = defined( 'WP_CRON_LOCK_TIMEOUT' ) ? WP_CRON_LOCK_TIMEOUT : 60;

if ( $network ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}

$sites = get_sites(
array(
'fields' => 'ids',
'number' => 0,
)
);

if ( empty( $sites ) ) {
WP_CLI::error( 'No sites found in the network.' );
}

// Remove network flag before passing to get_selected_cron_events.
$network_assoc_args = $assoc_args;
unset( $network_assoc_args['network'] );

$total_executed = 0;
$site_count = count( $sites );

foreach ( $sites as $site_id ) {
switch_to_blog( $site_id );

$doing_cron_value = null;

if ( $due_now ) {
$doing_cron_transient = get_transient( 'doing_cron' );
if ( is_numeric( $doing_cron_transient ) && (float) $doing_cron_transient > microtime( true ) - $lock_timeout ) {
WP_CLI::warning( 'A cron event run is already in progress; skipping.' );
return;
}
$doing_cron_value = sprintf( '%.22F', microtime( true ) );
set_transient( 'doing_cron', $doing_cron_value, $lock_timeout );
}

$events = self::get_selected_cron_events( $args, $network_assoc_args );

if ( ! is_wp_error( $events ) ) {
$total_executed += self::run_events( $events );
if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) {
delete_transient( 'doing_cron' );
}
} else {
if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) {
delete_transient( 'doing_cron' );
}
WP_CLI::debug( sprintf( 'No events found for site %d: %s', $site_id, $events->get_error_message() ), 'cron' );
}

restore_current_blog();
}

$message = sprintf(
'Executed a total of %d %s across %d %s.',
$total_executed,
Utils\pluralize( 'cron event', $total_executed ),
$site_count,
Utils\pluralize( 'site', $site_count )
);
WP_CLI::success( $message );
return;
}

$doing_cron_value = null;

if ( $due_now ) {
$lock_timeout = defined( 'WP_CRON_LOCK_TIMEOUT' ) ? WP_CRON_LOCK_TIMEOUT : 60;
$doing_cron_transient = get_transient( 'doing_cron' );
if ( is_numeric( $doing_cron_transient ) && (float) $doing_cron_transient > microtime( true ) - $lock_timeout ) {
WP_CLI::warning( 'A cron event run is already in progress; skipping.' );
Expand All @@ -268,18 +345,7 @@ public function run( $args, $assoc_args ) {
WP_CLI::error( $events );
}

$executed = 0;
foreach ( $events as $event ) {
WP_CLI::debug( sprintf( "Beginning execution of cron event '%s'.", $event->hook ), 'cron' );
$start = microtime( true );
$result = self::run_event( $event );
$total = round( microtime( true ) - $start, 3 );
++$executed;
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
if ( ! empty( $event->args ) ) {
WP_CLI::debug( sprintf( 'Arguments: %s', wp_json_encode( $event->args ) ), 'cron' );
}
}
$executed = self::run_events( $events );

if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) {
delete_transient( 'doing_cron' );
Expand Down Expand Up @@ -437,6 +503,30 @@ function ( $event ) use ( $decoded_args ) {
WP_CLI::success( sprintf( $message, $deleted ) );
}

/**
* Runs multiple cron events and logs their execution.
*
* @param array $events Array of event objects to run.
* @return int The number of events executed.
*/
private static function run_events( array $events ) {
$executed = 0;

foreach ( $events as $event ) {
WP_CLI::debug( sprintf( "Beginning execution of cron event '%s'.", $event->hook ), 'cron' );
$start = microtime( true );
self::run_event( $event );
$total = round( microtime( true ) - $start, 3 );
++$executed;
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
if ( ! empty( $event->args ) ) {
WP_CLI::debug( sprintf( 'Arguments: %s', wp_json_encode( $event->args ) ), 'cron' );
}
}

return $executed;
}

/**
* Executes an event immediately.
*
Expand Down Expand Up @@ -520,6 +610,10 @@ protected static function get_cron_events( $is_due_now = false ) {
);
}

if ( ! is_array( $crons ) ) {
return [];
}

foreach ( $crons as $time => $hooks ) {

// Incorrectly registered cron events can produce a string key.
Expand Down