PluginProbe
WP Crontrol / trunk
WP Crontrol vtrunk
1.21.2 trunk 0.1 0.2 0.3 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.1 1.10.0 1.11.0 1.12.0 1.12.1 1.13.0 1.13.1 1.13.2 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 All 57 releases
wp-crontrol / src / Event / Event.php

Event.php in WP Crontrol trunk, at src/Event/Event.php

372 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base class for cron events.
4 */
5
6 namespace Crontrol\Event;
7
8 use Crontrol\Context\FeatureContext;
9 use Crontrol\Context\UserContext;
10 use Crontrol\Event\PHPCronEvent;
11 use Crontrol\Event\URLCronEvent;
12 use Crontrol\Event\CoreCronEvent;
13 use Crontrol\Event\ActionSchedulerEvent;
14 use Crontrol\Event\StandardEvent;
15 use Crontrol\Exception\UnknownScheduleException;
16
17 /**
18 * Base class for cron events.
19 */
20 abstract class Event {
21 /**
22 * The hook name of the cron event.
23 *
24 * @var string
25 */
26 public string $hook;
27
28 /**
29 * The Unix timestamp when the event should run.
30 *
31 * @var int
32 */
33 public int $timestamp;
34
35 /**
36 * The event signature.
37 *
38 * @var string
39 */
40 public string $sig;
41
42 /**
43 * The arguments to pass to the hook's callback function.
44 *
45 * Note: This should normally be an array, but may contain other types if
46 * the cron data is corrupted or invalid. Check the has_invalid_args() method.
47 *
48 * @var mixed[]|mixed
49 */
50 public $args;
51
52 /**
53 * The schedule name or null for one-time events.
54 *
55 * @var string|null
56 */
57 public $schedule;
58
59 /**
60 * The interval time in seconds for the schedule. Only present for recurring events.
61 *
62 * @var int|null
63 */
64 public $interval;
65
66 /**
67 * Whether this event has invalid (non-array) args.
68 */
69 public bool $has_invalid_args = false;
70
71 /**
72 * Constructor.
73 *
74 * @param string $hook The hook name of the cron event.
75 * @param int $timestamp The Unix timestamp (UTC) when the event should run.
76 * @param string $sig The event signature.
77 * @param mixed[] $args The arguments to pass to the hook's callback function.
78 * @param string|null $schedule The schedule name or null for one-time events.
79 * @param int|null $interval The interval time in seconds for the schedule. Only present for recurring events.
80 */
81 protected function __construct( string $hook, int $timestamp, string $sig, $args, ?string $schedule, ?int $interval ) {
82 $this->hook = $hook;
83 $this->timestamp = $timestamp;
84 $this->sig = $sig;
85 $this->args = $args;
86 $this->schedule = $schedule;
87 $this->interval = $interval;
88 // Args should be an array but corrupted or invalid cron data may contain other types.
89 // @phpstan-ignore function.alreadyNarrowedType
90 $this->has_invalid_args = ! is_array( $args );
91 }
92
93 /**
94 * Factory method to create appropriate Event instance.
95 *
96 * @param string $hook The hook name of the cron event.
97 * @param int $timestamp The Unix timestamp (UTC) when the event should run.
98 * @param string $sig The event signature.
99 * @param mixed[] $args The arguments to pass to the hook's callback function.
100 * @param string|null $schedule The schedule name or null for one-time events.
101 * @param int|null $interval The interval time in seconds for the schedule. Only present for recurring events.
102 * @return self The appropriate Event instance.
103 * @phpstan-return (
104 * $hook is PHPCronEvent::HOOK_NAME ? PHPCronEvent :
105 * $hook is URLCronEvent::HOOK_NAME ? URLCronEvent :
106 * $hook is ActionSchedulerEvent::HOOK_NAME ? ActionSchedulerEvent :
107 * (CoreCronEvent|StandardEvent)
108 * )
109 */
110 public static function create( string $hook, int $timestamp, string $sig, $args, ?string $schedule, ?int $interval ): self {
111 if ( PHPCronEvent::HOOK_NAME === $hook ) {
112 return new PHPCronEvent( $hook, $timestamp, $sig, $args, $schedule, $interval );
113 }
114
115 if ( URLCronEvent::HOOK_NAME === $hook ) {
116 return new URLCronEvent( $hook, $timestamp, $sig, $args, $schedule, $interval );
117 }
118
119 if ( ActionSchedulerEvent::HOOK_NAME === $hook ) {
120 return new ActionSchedulerEvent( $hook, $timestamp, $sig, $args, $schedule, $interval );
121 }
122
123 if ( in_array( $hook, \Crontrol\get_all_core_hooks(), true ) ) {
124 return new CoreCronEvent( $hook, $timestamp, $sig, $args, $schedule, $interval );
125 }
126
127 return new StandardEvent( $hook, $timestamp, $sig, $args, $schedule, $interval );
128 }
129
130 /**
131 * Factory method to create a new empty Event instance with default values.
132 *
133 * @return self A new StandardEvent instance with default empty values.
134 */
135 public static function create_new(): self {
136 return self::create( '', time(), '', array(), null, null );
137 }
138
139 /**
140 * Factory method to create an immediate Event instance (timestamp = 1).
141 *
142 * @param string $hook The hook name of the cron event.
143 * @param mixed[] $args The arguments to pass to the hook's callback function.
144 * @return self The appropriate Event instance set to run immediately.
145 */
146 public static function create_immediate( string $hook, $args = array() ): self {
147 return self::create( $hook, 1, '', $args, null, null );
148 }
149
150 /**
151 * Check if this is a recurring event.
152 */
153 public function is_recurring(): bool {
154 return is_string( $this->schedule );
155 }
156
157 /**
158 * Get the registered callbacks for this event's hook.
159 *
160 * @return array<int,array<string,mixed>> Array of callbacks attached to the hook.
161 * @phpstan-return array<int,array{
162 * priority: int,
163 * callback: array<string,mixed>,
164 * }>
165 */
166 public function get_callbacks(): array {
167 return \Crontrol\get_hook_callbacks( $this->hook );
168 }
169
170 /**
171 * Get the next run time in local timezone.
172 *
173 * @param string $format The date format string. Defaults to 'c' (ISO 8601).
174 * @return string The formatted date in local timezone.
175 */
176 public function get_next_run_local( string $format = 'c' ): string {
177 return get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $this->timestamp ), $format );
178 }
179
180 /**
181 * Get the next run time in UTC.
182 *
183 * @param string $format The date format string. Defaults to 'c' (ISO 8601).
184 * @return string The formatted date in UTC.
185 */
186 public function get_next_run_utc( string $format = 'c' ): string {
187 return gmdate( $format, $this->timestamp );
188 }
189
190 /**
191 * Check if this event's hook is paused.
192 */
193 public function is_paused(): bool {
194 $paused = get_option( \Crontrol\PAUSED_OPTION );
195
196 if ( ! is_array( $paused ) ) {
197 return false;
198 }
199
200 return array_key_exists( $this->hook, $paused );
201 }
202
203 /**
204 * Check if this event is late (past its scheduled time by more than 10 minutes).
205 */
206 public function is_late(): bool {
207 $until = $this->timestamp - time();
208
209 return ( $until < ( 0 - ( 10 * MINUTE_IN_SECONDS ) ) );
210 }
211
212 /**
213 * Check if this event's schedule is too frequent (interval less than WP_CRON_LOCK_TIMEOUT).
214 */
215 public function is_too_frequent(): bool {
216 if ( ! $this->schedule ) {
217 return false;
218 }
219
220 $schedules = \Crontrol\Schedule\get();
221
222 if ( ! isset( $schedules[ $this->schedule ] ) ) {
223 return false;
224 }
225
226 return $schedules[ $this->schedule ]->is_too_frequent();
227 }
228
229 /**
230 * Check if this event has integrity failures (corrupted data).
231 */
232 public function integrity_failed(): bool {
233 return false;
234 }
235
236 /**
237 * Check if this event has any errors (syntax errors, URL errors, or integrity failures).
238 */
239 public function has_error(): bool {
240 return $this->has_invalid_args;
241 }
242
243 /**
244 * Check if this event has invalid (non-array) args.
245 */
246 public function has_invalid_args(): bool {
247 return $this->has_invalid_args;
248 }
249
250 /**
251 * Get the schedule name for this event.
252 *
253 * @return string The schedule display name.
254 * @throws UnknownScheduleException If schedule is unknown.
255 */
256 public function get_schedule_name(): string {
257 if ( ! $this->is_recurring() ) {
258 return __( 'Non-repeating', 'wp-crontrol' );
259 }
260
261 $schedules = \Crontrol\Schedule\get();
262
263 if ( isset( $schedules[ $this->schedule ] ) ) {
264 return $schedules[ $this->schedule ]->display;
265 }
266
267 throw new UnknownScheduleException(
268 sprintf(
269 /* translators: %s: Schedule name */
270 __( 'Unknown schedule (%s)', 'wp-crontrol' ),
271 $this->schedule
272 )
273 );
274 }
275
276 /**
277 * Check if this event's hook name can be edited.
278 */
279 public function hook_name_editable(): bool {
280 return true;
281 }
282
283 /**
284 * Returns the descriptive label for this event's hook name.
285 *
286 * @return string The HTML for the hook name label.
287 */
288 public function get_hook_name_label(): string {
289 return esc_html( $this->hook );
290 }
291
292 /**
293 * Returns the HTML for any error status to show alongside this event's hook name.
294 *
295 * @return string The HTML for the error status, or an empty string if there is no error.
296 */
297 public function get_error_status_html(): string {
298 return '';
299 }
300
301 /**
302 * Check if this event is scheduled to run immediately via "Run now".
303 *
304 * Events with timestamp 1 are scheduled to run immediately and only appear
305 * in the event list when there's a problem with the event runner.
306 */
307 public function is_immediate(): bool {
308 return $this->timestamp === 1;
309 }
310
311 /**
312 * Determines if this event can be edited given the current user and feature context.
313 *
314 * @param UserContext $user User capability context.
315 * @param FeatureContext $features Feature flag context.
316 */
317 abstract public function editable( UserContext $user, FeatureContext $features ): bool;
318
319 /**
320 * Determines if this event can be run given the current user and feature context.
321 *
322 * @param UserContext $user User capability context.
323 * @param FeatureContext $features Feature flag context.
324 */
325 abstract public function runnable( UserContext $user, FeatureContext $features ): bool;
326
327 /**
328 * Determines if this event is persistent and cannot be deleted regardless of permissions.
329 */
330 public function persistent(): bool {
331 return false;
332 }
333
334 /**
335 * Gets the message explaining why this event is persistent.
336 *
337 * Only called if persistent() returns true.
338 *
339 * @return string The persistent reason message.
340 */
341 public function get_persistent_message(): string {
342 return '';
343 }
344
345 /**
346 * Determines if this event can be deleted given the current user and feature context.
347 *
348 * @param UserContext $user User capability context.
349 * @param FeatureContext $features Feature flag context.
350 */
351 abstract public function deletable( UserContext $user, FeatureContext $features ): bool;
352
353 /**
354 * Determines if this event can be paused.
355 */
356 abstract public function pausable(): bool;
357
358 /**
359 * Gets the display representation of this event's arguments.
360 *
361 * @return string The formatted arguments for display.
362 */
363 abstract public function get_args_display(): string;
364
365 /**
366 * Determines if this event type is currently enabled in the feature context.
367 *
368 * @param FeatureContext $features Feature flag context.
369 */
370 abstract public function is_enabled( FeatureContext $features ): bool;
371 }
372