Plugin Directory

source: wp-crontrol/trunk/src/Event/Event.php

Last change on this file was 3449050, checked in by johnbillion, 2 months ago

Update to version 1.21.0 from GitHub

File size: 9.7 KB
Line 
1<?php
2/**
3 * Base class for cron events.
4 */
5
6namespace Crontrol\Event;
7
8use Crontrol\Context\FeatureContext;
9use Crontrol\Context\UserContext;
10use Crontrol\Event\PHPCronEvent;
11use Crontrol\Event\URLCronEvent;
12use Crontrol\Event\CoreCronEvent;
13use Crontrol\Event\ActionSchedulerEvent;
14use Crontrol\Event\StandardEvent;
15use Crontrol\Exception\UnknownScheduleException;
16
17/**
18 * Base class for cron events.
19 */
20abstract 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         * Check if this event is scheduled to run immediately via "Run now".
285         *
286         * Events with timestamp 1 are scheduled to run immediately and only appear
287         * in the event list when there's a problem with the event runner.
288         */
289        public function is_immediate(): bool {
290                return $this->timestamp === 1;
291        }
292
293        /**
294         * Determines if this event can be edited given the current user and feature context.
295         *
296         * @param UserContext $user User capability context.
297         * @param FeatureContext $features Feature flag context.
298         */
299        abstract public function editable( UserContext $user, FeatureContext $features ): bool;
300
301        /**
302         * Determines if this event can be run given the current user and feature context.
303         *
304         * @param UserContext $user User capability context.
305         * @param FeatureContext $features Feature flag context.
306         */
307        abstract public function runnable( UserContext $user, FeatureContext $features ): bool;
308
309        /**
310         * Determines if this event is persistent and cannot be deleted regardless of permissions.
311         */
312        public function persistent(): bool {
313                return false;
314        }
315
316        /**
317         * Gets the message explaining why this event is persistent.
318         *
319         * Only called if persistent() returns true.
320         *
321         * @return string The persistent reason message.
322         */
323        public function get_persistent_message(): string {
324                return '';
325        }
326
327        /**
328         * Determines if this event can be deleted given the current user and feature context.
329         *
330         * @param UserContext $user User capability context.
331         * @param FeatureContext $features Feature flag context.
332         */
333        abstract public function deletable( UserContext $user, FeatureContext $features ): bool;
334
335        /**
336         * Determines if this event can be paused.
337         */
338        abstract public function pausable(): bool;
339
340        /**
341         * Gets the display representation of this event's arguments.
342         *
343         * @return string The formatted arguments for display.
344         */
345        abstract public function get_args_display(): string;
346
347        /**
348         * Determines if this event type is currently enabled in the feature context.
349         *
350         * @param FeatureContext $features Feature flag context.
351         */
352        abstract public function is_enabled( FeatureContext $features ): bool;
353}
Note: See TracBrowser for help on using the repository browser.