Skip to content

Commit b0af5ff

Browse files
CopilotswissspidyCopilot
authored
Add actions field to wp cron event list (#124)
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@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 0c11c38 commit b0af5ff

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

features/cron-event.feature

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

162+
Scenario: List cron events with actions field
163+
Given a wp-content/mu-plugins/test-cron-actions.php file:
164+
"""
165+
<?php
166+
add_action( 'wp_cli_test_hook', 'wp_cli_test_function' );
167+
add_action( 'wp_cli_test_hook', array( 'MyTestClass', 'my_method' ) );
168+
add_action( 'wp_cli_test_hook_closure', function() {
169+
// Test closure
170+
} );
171+
172+
function wp_cli_test_function() {
173+
// Test function
174+
}
175+
176+
class MyTestClass {
177+
public static function my_method() {
178+
// Test method
179+
}
180+
}
181+
"""
182+
183+
When I run `wp cron event schedule wp_cli_test_hook now`
184+
Then STDOUT should contain:
185+
"""
186+
Success: Scheduled event with hook 'wp_cli_test_hook'
187+
"""
188+
189+
When I run `wp cron event list --fields=hook,actions --format=csv`
190+
Then STDOUT should contain:
191+
"""
192+
wp_cli_test_function
193+
"""
194+
And STDOUT should contain:
195+
"""
196+
MyTestClass::my_method
197+
"""
198+
199+
When I run `wp cron event list --hook=wp_cli_test_hook --fields=hook,actions --format=json`
200+
Then STDOUT should be JSON containing:
201+
"""
202+
[{"hook":"wp_cli_test_hook"}]
203+
"""
204+
And STDOUT should contain:
205+
"""
206+
wp_cli_test_function
207+
"""
208+
209+
When I run `wp cron event schedule wp_cli_test_hook_closure now`
210+
Then STDOUT should contain:
211+
"""
212+
Success: Scheduled event with hook 'wp_cli_test_hook_closure'
213+
"""
214+
215+
When I run `wp cron event list --hook=wp_cli_test_hook_closure --fields=hook,actions --format=csv`
216+
Then STDOUT should contain:
217+
"""
218+
Closure
219+
"""
220+
221+
When I run `wp cron event schedule wp_cli_test_hook_no_actions now`
222+
Then STDOUT should contain:
223+
"""
224+
Success: Scheduled event with hook 'wp_cli_test_hook_no_actions'
225+
"""
226+
227+
When I run `wp cron event list --hook=wp_cli_test_hook_no_actions --fields=hook,actions --format=csv`
228+
Then STDOUT should contain:
229+
"""
230+
None
231+
"""
232+
162233
Scenario: Confirm that cron event run in debug mode shows the start of events
163234
When I try `wp cron event run wp_version_check --debug=cron`
164235
Then STDOUT should contain:

src/Cron_Event_Command.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class Cron_Event_Command extends WP_CLI_Command {
8585
* * schedule
8686
* * interval
8787
* * next_run
88+
* * actions
8889
*
8990
* ## EXAMPLES
9091
*
@@ -113,6 +114,14 @@ public function list_( $args, $assoc_args ) {
113114
$events = array();
114115
}
115116

117+
// Populate actions field only if requested
118+
$requested_fields = $formatter->fields;
119+
if ( ! empty( $requested_fields ) && in_array( 'actions', $requested_fields, true ) ) {
120+
foreach ( $events as $event ) {
121+
$event->actions = self::get_hook_actions( $event->hook );
122+
}
123+
}
124+
116125
foreach ( $events as $key => $event ) {
117126
foreach ( $this->fields as $field ) {
118127
if ( ! empty( $assoc_args[ $field ] ) && $event->{$field} !== $assoc_args[ $field ] ) {
@@ -399,6 +408,7 @@ protected static function format_event( stdClass $event ) {
399408
$event->next_run = get_date_from_gmt( date( 'Y-m-d H:i:s', $event->time ), self::$time_format ); //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
400409
$event->next_run_gmt = date( self::$time_format, $event->time ); //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
401410
$event->next_run_relative = self::interval( $event->time - time() );
411+
$event->actions = '';
402412

403413
return $event;
404414
}
@@ -607,4 +617,92 @@ private static function interval( $since ) {
607617
private function get_formatter( &$assoc_args ) {
608618
return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'event' );
609619
}
620+
621+
/**
622+
* Gets the actions (callbacks) registered for a specific hook.
623+
*
624+
* @param string $hook_name The name of the hook.
625+
* @return string A comma-separated list of action callbacks, or 'None' if no actions are registered.
626+
*/
627+
protected static function get_hook_actions( $hook_name ) {
628+
static $cache = array();
629+
630+
if ( isset( $cache[ $hook_name ] ) ) {
631+
return $cache[ $hook_name ];
632+
}
633+
634+
global $wp_filter;
635+
636+
if ( ! isset( $wp_filter[ $hook_name ] ) ) {
637+
$cache[ $hook_name ] = 'None';
638+
return $cache[ $hook_name ];
639+
}
640+
641+
$hook = $wp_filter[ $hook_name ];
642+
643+
// Get callbacks from the WP_Hook object (WordPress 4.7+)
644+
if ( $hook instanceof \WP_Hook ) {
645+
$callbacks = $hook->callbacks;
646+
} else {
647+
// Fallback for older WordPress versions
648+
$callbacks = $hook;
649+
}
650+
651+
if ( empty( $callbacks ) ) {
652+
$cache[ $hook_name ] = 'None';
653+
return $cache[ $hook_name ];
654+
}
655+
656+
ksort( $callbacks );
657+
658+
$actions = array();
659+
660+
// Iterate through all priorities
661+
foreach ( $callbacks as $priority => $priority_callbacks ) {
662+
foreach ( $priority_callbacks as $callback_info ) {
663+
if ( ! isset( $callback_info['function'] ) ) {
664+
continue;
665+
}
666+
$callback = $callback_info['function'];
667+
$actions[] = self::format_callback( $callback );
668+
}
669+
}
670+
671+
$result = empty( $actions ) ? 'None' : implode( ', ', $actions );
672+
$cache[ $hook_name ] = $result;
673+
return $cache[ $hook_name ];
674+
}
675+
676+
/**
677+
* Formats a callback into a readable string.
678+
*
679+
* @param callable $callback The callback to format.
680+
* @return string A formatted string representing the callback.
681+
*/
682+
protected static function format_callback( $callback ) {
683+
if ( is_string( $callback ) ) {
684+
return $callback;
685+
} elseif ( is_array( $callback ) && count( $callback ) === 2 ) {
686+
/**
687+
* @var array{0: string, 1: string} $callback
688+
*/
689+
$class = $callback[0];
690+
$method = $callback[1];
691+
692+
if ( is_object( $class ) ) {
693+
$class_name = get_class( $class );
694+
} else {
695+
$class_name = $class;
696+
}
697+
698+
return $class_name . '::' . $method;
699+
} elseif ( $callback instanceof \Closure ) {
700+
return 'Closure';
701+
} elseif ( is_object( $callback ) ) {
702+
return get_class( $callback ) . '::__invoke';
703+
}
704+
705+
// Fallback for unknown callback types
706+
return 'Unknown';
707+
}
610708
}

0 commit comments

Comments
 (0)