Skip to content

Commit 51de942

Browse files
Copilotswissspidy
andauthored
Add --network flag to wp cron event run for multisite support (#125)
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Pascal Birchler <pascalb@google.com>
1 parent 990a21f commit 51de942

File tree

2 files changed

+146
-13
lines changed

2 files changed

+146
-13
lines changed

features/cron-event.feature

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,45 @@ Feature: Manage WP Cron events
159159
Debug: Arguments:
160160
"""
161161

162+
Scenario: Run cron events with --network flag on non-multisite
163+
When I try `wp cron event run --due-now --network`
164+
Then STDERR should be:
165+
"""
166+
Error: This is not a multisite installation.
167+
"""
168+
And the return code should be 1
169+
170+
Scenario: Run cron events with --network flag on multisite
171+
Given a WP multisite subdirectory install
172+
And I run `wp site create --slug=site2`
173+
And I run `wp site create --slug=site3`
174+
175+
When I run `wp cron event schedule wp_cli_network_test now`
176+
Then STDOUT should contain:
177+
"""
178+
Success: Scheduled event with hook 'wp_cli_network_test'
179+
"""
180+
181+
When I run `wp --url=example.com/site2 cron event schedule wp_cli_network_test_site2 now`
182+
Then STDOUT should contain:
183+
"""
184+
Success: Scheduled event with hook 'wp_cli_network_test_site2'
185+
"""
186+
187+
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`
188+
Then STDOUT should contain:
189+
"""
190+
Executed the cron event 'wp_cli_network_test'
191+
"""
192+
And STDOUT should contain:
193+
"""
194+
Executed the cron event 'wp_cli_network_test_site2'
195+
"""
196+
And STDOUT should contain:
197+
"""
198+
Success: Executed a total of 2 cron events across 3 sites.
199+
"""
200+
162201
Scenario: List cron events with actions field
163202
Given a wp-content/mu-plugins/test-cron-actions.php file:
164203
"""

src/Cron_Event_Command.php

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -235,21 +235,98 @@ public function schedule( $args, $assoc_args ) {
235235
* [--all]
236236
* : Run all hooks.
237237
*
238+
* [--network]
239+
* : Run hooks across all sites in a multisite installation.
240+
*
238241
* ## EXAMPLES
239242
*
240243
* # Run all cron events due right now
241244
* $ wp cron event run --due-now
242245
* Executed the cron event 'cron_test_1' in 0.01s.
243246
* Executed the cron event 'cron_test_2' in 0.006s.
244247
* Success: Executed a total of 2 cron events.
248+
*
249+
* # Run all cron events due right now across all sites in a multisite
250+
* $ wp cron event run --due-now --network
251+
* Executed the cron event 'cron_test_1' in 0.01s.
252+
* Executed the cron event 'cron_test_2' in 0.006s.
253+
* Success: Executed a total of 2 cron events across 3 sites.
245254
*/
246255
public function run( $args, $assoc_args ) {
247256
$due_now = Utils\get_flag_value( $assoc_args, 'due-now' );
248257

258+
$network = Utils\get_flag_value( $assoc_args, 'network' );
259+
$lock_timeout = defined( 'WP_CRON_LOCK_TIMEOUT' ) ? WP_CRON_LOCK_TIMEOUT : 60;
260+
261+
if ( $network ) {
262+
if ( ! is_multisite() ) {
263+
WP_CLI::error( 'This is not a multisite installation.' );
264+
}
265+
266+
$sites = get_sites(
267+
array(
268+
'fields' => 'ids',
269+
'number' => 0,
270+
)
271+
);
272+
273+
if ( empty( $sites ) ) {
274+
WP_CLI::error( 'No sites found in the network.' );
275+
}
276+
277+
// Remove network flag before passing to get_selected_cron_events.
278+
$network_assoc_args = $assoc_args;
279+
unset( $network_assoc_args['network'] );
280+
281+
$total_executed = 0;
282+
$site_count = count( $sites );
283+
284+
foreach ( $sites as $site_id ) {
285+
switch_to_blog( $site_id );
286+
287+
$doing_cron_value = null;
288+
289+
if ( $due_now ) {
290+
$doing_cron_transient = get_transient( 'doing_cron' );
291+
if ( is_numeric( $doing_cron_transient ) && (float) $doing_cron_transient > microtime( true ) - $lock_timeout ) {
292+
WP_CLI::warning( 'A cron event run is already in progress; skipping.' );
293+
return;
294+
}
295+
$doing_cron_value = sprintf( '%.22F', microtime( true ) );
296+
set_transient( 'doing_cron', $doing_cron_value, $lock_timeout );
297+
}
298+
299+
$events = self::get_selected_cron_events( $args, $network_assoc_args );
300+
301+
if ( ! is_wp_error( $events ) ) {
302+
$total_executed += self::run_events( $events );
303+
if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) {
304+
delete_transient( 'doing_cron' );
305+
}
306+
} else {
307+
if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) {
308+
delete_transient( 'doing_cron' );
309+
}
310+
WP_CLI::debug( sprintf( 'No events found for site %d: %s', $site_id, $events->get_error_message() ), 'cron' );
311+
}
312+
313+
restore_current_blog();
314+
}
315+
316+
$message = sprintf(
317+
'Executed a total of %d %s across %d %s.',
318+
$total_executed,
319+
Utils\pluralize( 'cron event', $total_executed ),
320+
$site_count,
321+
Utils\pluralize( 'site', $site_count )
322+
);
323+
WP_CLI::success( $message );
324+
return;
325+
}
326+
249327
$doing_cron_value = null;
250328

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

271-
$executed = 0;
272-
foreach ( $events as $event ) {
273-
WP_CLI::debug( sprintf( "Beginning execution of cron event '%s'.", $event->hook ), 'cron' );
274-
$start = microtime( true );
275-
$result = self::run_event( $event );
276-
$total = round( microtime( true ) - $start, 3 );
277-
++$executed;
278-
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
279-
if ( ! empty( $event->args ) ) {
280-
WP_CLI::debug( sprintf( 'Arguments: %s', wp_json_encode( $event->args ) ), 'cron' );
281-
}
282-
}
348+
$executed = self::run_events( $events );
283349

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

506+
/**
507+
* Runs multiple cron events and logs their execution.
508+
*
509+
* @param array $events Array of event objects to run.
510+
* @return int The number of events executed.
511+
*/
512+
private static function run_events( array $events ) {
513+
$executed = 0;
514+
515+
foreach ( $events as $event ) {
516+
WP_CLI::debug( sprintf( "Beginning execution of cron event '%s'.", $event->hook ), 'cron' );
517+
$start = microtime( true );
518+
self::run_event( $event );
519+
$total = round( microtime( true ) - $start, 3 );
520+
++$executed;
521+
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
522+
if ( ! empty( $event->args ) ) {
523+
WP_CLI::debug( sprintf( 'Arguments: %s', wp_json_encode( $event->args ) ), 'cron' );
524+
}
525+
}
526+
527+
return $executed;
528+
}
529+
440530
/**
441531
* Executes an event immediately.
442532
*
@@ -520,6 +610,10 @@ protected static function get_cron_events( $is_due_now = false ) {
520610
);
521611
}
522612

613+
if ( ! is_array( $crons ) ) {
614+
return [];
615+
}
616+
523617
foreach ( $crons as $time => $hooks ) {
524618

525619
// Incorrectly registered cron events can produce a string key.

0 commit comments

Comments
 (0)